From 589b0e3d755e8887747ee1c7ea841de2232b9899 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 10:16:11 +0000 Subject: [PATCH 001/408] fix(parsing): correctly handle nested discriminated unions --- src/openai/_models.py | 11 +++++++---- tests/test_models.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 065e8da760..f347a81dac 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, Type, Tuple, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( + List, Unpack, Literal, ClassVar, @@ -391,7 +392,7 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: if type_ is None: raise RuntimeError(f"Unexpected field type is None for {key}") - return construct_type(value=value, type_=type_) + return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) def is_basemodel(type_: type) -> bool: @@ -445,7 +446,7 @@ def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: return cast(_T, construct_type(value=value, type_=type_)) -def construct_type(*, value: object, type_: object) -> object: +def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: """Loose coercion to the expected type with construction of nested values. If the given value does not match the expected type then it is returned as-is. @@ -463,8 +464,10 @@ def construct_type(*, value: object, type_: object) -> object: type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(type_): - meta: tuple[Any, ...] = get_args(type_)[1:] + if metadata is not None: + meta: tuple[Any, ...] = tuple(metadata) + elif is_annotated_type(type_): + meta = get_args(type_)[1:] type_ = extract_type_arg(type_, 0) else: meta = tuple() diff --git a/tests/test_models.py b/tests/test_models.py index 440e17a08c..7262f45006 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -889,3 +889,48 @@ class ModelB(BaseModel): ) assert isinstance(m, ModelB) + + +def test_nested_discriminated_union() -> None: + class InnerType1(BaseModel): + type: Literal["type_1"] + + class InnerModel(BaseModel): + inner_value: str + + class InnerType2(BaseModel): + type: Literal["type_2"] + some_inner_model: InnerModel + + class Type1(BaseModel): + base_type: Literal["base_type_1"] + value: Annotated[ + Union[ + InnerType1, + InnerType2, + ], + PropertyInfo(discriminator="type"), + ] + + class Type2(BaseModel): + base_type: Literal["base_type_2"] + + T = Annotated[ + Union[ + Type1, + Type2, + ], + PropertyInfo(discriminator="base_type"), + ] + + model = construct_type( + type_=T, + value={ + "base_type": "base_type_1", + "value": { + "type": "type_2", + }, + }, + ) + assert isinstance(model, Type1) + assert isinstance(model.value, InnerType2) From fa8e1cb37681e06da4239d8011687b7dc105365a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 10:16:40 +0000 Subject: [PATCH 002/408] release: 1.93.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 02609a40fd..074ba77967 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.93.2" + ".": "1.93.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 92645c8e02..00931cdb79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.93.3 (2025-07-09) + +Full Changelog: [v1.93.2...v1.93.3](https://github.com/openai/openai-python/compare/v1.93.2...v1.93.3) + +### Bug Fixes + +* **parsing:** correctly handle nested discriminated unions ([fc8a677](https://github.com/openai/openai-python/commit/fc8a67715d8f1b45d8639b8b6f9f6590fe358734)) + ## 1.93.2 (2025-07-08) Full Changelog: [v1.93.1...v1.93.2](https://github.com/openai/openai-python/compare/v1.93.1...v1.93.2) diff --git a/pyproject.toml b/pyproject.toml index d1fda0244b..4f3642c922 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.93.2" +version = "1.93.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a5ddf48daf..828e93d58a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.93.2" # x-release-please-version +__version__ = "1.93.3" # x-release-please-version From 361dc3274b6b48847860cb92bfccb31dd0b546ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Sch=C3=BCller?= Date: Thu, 10 Jul 2025 14:48:09 +0200 Subject: [PATCH 003/408] feat(api): return better error message on missing embedding (#2369) --- src/openai/resources/embeddings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 553dacc284..609f33f3b4 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -112,6 +112,9 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: # don't modify the response object if a user explicitly asked for a format return obj + if not obj.data: + raise ValueError("No embedding data received") + for embedding in obj.data: data = cast(object, embedding.embedding) if not isinstance(data, str): @@ -228,6 +231,9 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: # don't modify the response object if a user explicitly asked for a format return obj + if not obj.data: + raise ValueError("No embedding data received") + for embedding in obj.data: data = cast(object, embedding.embedding) if not isinstance(data, str): From 4d5fe48ee4bb44064c786d175084b7ba7f1bd792 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 12:48:36 +0000 Subject: [PATCH 004/408] release: 1.94.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 074ba77967..6db20a9bfb 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.93.3" + ".": "1.94.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 00931cdb79..7c99b6d6c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.94.0 (2025-07-10) + +Full Changelog: [v1.93.3...v1.94.0](https://github.com/openai/openai-python/compare/v1.93.3...v1.94.0) + +### Features + +* **api:** return better error message on missing embedding ([#2369](https://github.com/openai/openai-python/issues/2369)) ([e53464a](https://github.com/openai/openai-python/commit/e53464ae95f6a041f3267762834e6156c5ce1b57)) + ## 1.93.3 (2025-07-09) Full Changelog: [v1.93.2...v1.93.3](https://github.com/openai/openai-python/compare/v1.93.2...v1.93.3) diff --git a/pyproject.toml b/pyproject.toml index 4f3642c922..2c87a67c77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.93.3" +version = "1.94.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 828e93d58a..9ed696d5dd 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.93.3" # x-release-please-version +__version__ = "1.94.0" # x-release-please-version From db5c35049accb05f5fb03791ef9c12547fd309a7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 13:34:57 -0500 Subject: [PATCH 005/408] release: 1.95.0 (#2456) * chore(readme): fix version rendering on pypi * feat(api): add file_url, fix event ID * release: 1.95.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +-- CHANGELOG.md | 13 +++++ README.md | 3 +- pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/types/audio/transcription.py | 2 +- .../types/audio/transcription_verbose.py | 2 +- ...put_audio_transcription_completed_event.py | 52 +++++++++++++++++-- src/openai/types/file_object.py | 11 +++- .../types/responses/response_input_file.py | 3 ++ .../responses/response_input_file_param.py | 3 ++ ...response_mcp_call_arguments_delta_event.py | 4 +- .../response_mcp_call_arguments_done_event.py | 4 +- ...onse_output_text_annotation_added_event.py | 4 +- src/openai/types/responses/tool.py | 3 ++ src/openai/types/responses/tool_param.py | 3 ++ 17 files changed, 99 insertions(+), 20 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6db20a9bfb..9a75280778 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.94.0" + ".": "1.95.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 535155f4ae..816f05df5c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a473967d1766dc155994d932fbc4a5bcbd1c140a37c20d0a4065e1bf0640536d.yml -openapi_spec_hash: 67cdc62b0d6c8b1de29b7dc54b265749 -config_hash: 7b53f96f897ca1b3407a5341a6f820db +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2d116cda53321baa3479e628512def723207a81eb1cdaebb542bd0555e563bda.yml +openapi_spec_hash: 809d958fec261a32004a4b026b718793 +config_hash: e74d6791681e3af1b548748ff47a22c2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c99b6d6c8..f5c49d637f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.95.0 (2025-07-10) + +Full Changelog: [v1.94.0...v1.95.0](https://github.com/openai/openai-python/compare/v1.94.0...v1.95.0) + +### Features + +* **api:** add file_url, fix event ID ([265e216](https://github.com/openai/openai-python/commit/265e216396196d66cdfb5f92c5ef1a2a6ff27b5b)) + + +### Chores + +* **readme:** fix version rendering on pypi ([1eee5ca](https://github.com/openai/openai-python/commit/1eee5cabf2fd93877cd3ba85d0c6ed2ffd5f159f)) + ## 1.94.0 (2025-07-10) Full Changelog: [v1.93.3...v1.94.0](https://github.com/openai/openai-python/compare/v1.93.3...v1.94.0) diff --git a/README.md b/README.md index b38ef578d2..d09de14f3c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # OpenAI Python API library -[![PyPI version]()](https://pypi.org/project/openai/) + +[![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, diff --git a/pyproject.toml b/pyproject.toml index 2c87a67c77..774f1a35b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.94.0" +version = "1.95.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9ed696d5dd..342202129c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.94.0" # x-release-please-version +__version__ = "1.95.0" # x-release-please-version diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index 7115eb9edb..4c5882152d 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -46,7 +46,7 @@ class UsageTokens(BaseModel): class UsageDuration(BaseModel): - duration: float + seconds: float """Duration of the input audio in seconds.""" type: Literal["duration"] diff --git a/src/openai/types/audio/transcription_verbose.py b/src/openai/types/audio/transcription_verbose.py index cc6d769a65..addda71ec6 100644 --- a/src/openai/types/audio/transcription_verbose.py +++ b/src/openai/types/audio/transcription_verbose.py @@ -11,7 +11,7 @@ class Usage(BaseModel): - duration: float + seconds: float """Duration of the input audio in seconds.""" type: Literal["duration"] diff --git a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py index 469811693c..e7c457d4b2 100644 --- a/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py +++ b/src/openai/types/beta/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -1,11 +1,54 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional -from typing_extensions import Literal +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias from ...._models import BaseModel -__all__ = ["ConversationItemInputAudioTranscriptionCompletedEvent", "Logprob"] +__all__ = [ + "ConversationItemInputAudioTranscriptionCompletedEvent", + "Usage", + "UsageTranscriptTextUsageTokens", + "UsageTranscriptTextUsageTokensInputTokenDetails", + "UsageTranscriptTextUsageDuration", + "Logprob", +] + + +class UsageTranscriptTextUsageTokensInputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class UsageTranscriptTextUsageTokens(BaseModel): + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageTranscriptTextUsageTokensInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class UsageTranscriptTextUsageDuration(BaseModel): + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +Usage: TypeAlias = Union[UsageTranscriptTextUsageTokens, UsageTranscriptTextUsageDuration] class Logprob(BaseModel): @@ -37,5 +80,8 @@ class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): The event type, must be `conversation.item.input_audio_transcription.completed`. """ + usage: Usage + """Usage statistics for the transcription.""" + logprobs: Optional[List[Logprob]] = None """The log probabilities of the transcription.""" diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py index 1d65e6987d..883c2de019 100644 --- a/src/openai/types/file_object.py +++ b/src/openai/types/file_object.py @@ -25,12 +25,19 @@ class FileObject(BaseModel): """The object type, which is always `file`.""" purpose: Literal[ - "assistants", "assistants_output", "batch", "batch_output", "fine-tune", "fine-tune-results", "vision" + "assistants", + "assistants_output", + "batch", + "batch_output", + "fine-tune", + "fine-tune-results", + "vision", + "user_data", ] """The intended purpose of the file. Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, - `fine-tune`, `fine-tune-results` and `vision`. + `fine-tune`, `fine-tune-results`, `vision`, and `user_data`. """ status: Literal["uploaded", "processed", "error"] diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py index 00b35dc844..1eecd6a2b6 100644 --- a/src/openai/types/responses/response_input_file.py +++ b/src/openai/types/responses/response_input_file.py @@ -18,5 +18,8 @@ class ResponseInputFile(BaseModel): file_id: Optional[str] = None """The ID of the file to be sent to the model.""" + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + filename: Optional[str] = None """The name of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py index 61ae46f0cb..0b5f513ec6 100644 --- a/src/openai/types/responses/response_input_file_param.py +++ b/src/openai/types/responses/response_input_file_param.py @@ -18,5 +18,8 @@ class ResponseInputFileParam(TypedDict, total=False): file_id: Optional[str] """The ID of the file to be sent to the model.""" + file_url: str + """The URL of the file to be sent to the model.""" + filename: str """The name of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py index d6651e6999..8481506dc3 100644 --- a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py +++ b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py @@ -20,5 +20,5 @@ class ResponseMcpCallArgumentsDeltaEvent(BaseModel): sequence_number: int """The sequence number of this event.""" - type: Literal["response.mcp_call.arguments_delta"] - """The type of the event. Always 'response.mcp_call.arguments_delta'.""" + type: Literal["response.mcp_call_arguments.delta"] + """The type of the event. Always 'response.mcp_call_arguments.delta'.""" diff --git a/src/openai/types/responses/response_mcp_call_arguments_done_event.py b/src/openai/types/responses/response_mcp_call_arguments_done_event.py index a7ce46ad36..4be09d4862 100644 --- a/src/openai/types/responses/response_mcp_call_arguments_done_event.py +++ b/src/openai/types/responses/response_mcp_call_arguments_done_event.py @@ -20,5 +20,5 @@ class ResponseMcpCallArgumentsDoneEvent(BaseModel): sequence_number: int """The sequence number of this event.""" - type: Literal["response.mcp_call.arguments_done"] - """The type of the event. Always 'response.mcp_call.arguments_done'.""" + type: Literal["response.mcp_call_arguments.done"] + """The type of the event. Always 'response.mcp_call_arguments.done'.""" diff --git a/src/openai/types/responses/response_output_text_annotation_added_event.py b/src/openai/types/responses/response_output_text_annotation_added_event.py index ce96790c92..62d8f72863 100644 --- a/src/openai/types/responses/response_output_text_annotation_added_event.py +++ b/src/openai/types/responses/response_output_text_annotation_added_event.py @@ -26,5 +26,5 @@ class ResponseOutputTextAnnotationAddedEvent(BaseModel): sequence_number: int """The sequence number of this event.""" - type: Literal["response.output_text_annotation.added"] - """The type of the event. Always 'response.output_text_annotation.added'.""" + type: Literal["response.output_text.annotation.added"] + """The type of the event. Always 'response.output_text.annotation.added'.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 904c474e40..9c1573bda9 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -79,6 +79,9 @@ class Mcp(BaseModel): require_approval: Optional[McpRequireApproval] = None """Specify which of the MCP server's tools require approval.""" + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): type: Literal["auto"] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 4174560d42..493a1dad9c 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -80,6 +80,9 @@ class Mcp(TypedDict, total=False): require_approval: Optional[McpRequireApproval] """Specify which of the MCP server's tools require approval.""" + server_description: str + """Optional description of the MCP server, used to provide more context.""" + class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): type: Required[Literal["auto"]] From fcbb59831c12e9d0a1dae1880d4f650c57de5294 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:12:33 +0000 Subject: [PATCH 006/408] fix(client): don't send Content-Type header on GET requests --- pyproject.toml | 2 +- src/openai/_base_client.py | 11 +++++++++-- tests/test_client.py | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 774f1a35b0..f423907080 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ Repository = "https://github.com/openai/openai-python" openai = "openai.cli:main" [project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"] realtime = ["websockets >= 13, < 16"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 0a6385a7b5..3fe669259f 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -531,6 +531,15 @@ def _build_request( # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + kwargs["json"] = json_data if is_given(json_data) else None + kwargs["files"] = files + else: + headers.pop("Content-Type", None) + kwargs.pop("data", None) + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, @@ -542,8 +551,6 @@ def _build_request( # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, - json=json_data if is_given(json_data) else None, - files=files, **kwargs, ) diff --git a/tests/test_client.py b/tests/test_client.py index 988e5d994c..ccda50a7f0 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -463,7 +463,7 @@ def test_request_extra_query(self) -> None: def test_multipart_repeating_array(self, client: OpenAI) -> None: request = client._build_request( FinalRequestOptions.construct( - method="get", + method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, @@ -1348,7 +1348,7 @@ def test_request_extra_query(self) -> None: def test_multipart_repeating_array(self, async_client: AsyncOpenAI) -> None: request = async_client._build_request( FinalRequestOptions.construct( - method="get", + method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, From 0fa4028ac5b20c49aa0d3ed69dea2dcf277db574 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 18:29:28 +0000 Subject: [PATCH 007/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 816f05df5c..0a24d32759 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2d116cda53321baa3479e628512def723207a81eb1cdaebb542bd0555e563bda.yml openapi_spec_hash: 809d958fec261a32004a4b026b718793 -config_hash: e74d6791681e3af1b548748ff47a22c2 +config_hash: 00b55237774c015fc35f58d2820759a9 From 043589aebf4848dfa977f2b9d0a40a2de0dde95e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 18:32:46 +0000 Subject: [PATCH 008/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 0a24d32759..295b77b5af 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2d116cda53321baa3479e628512def723207a81eb1cdaebb542bd0555e563bda.yml openapi_spec_hash: 809d958fec261a32004a4b026b718793 -config_hash: 00b55237774c015fc35f58d2820759a9 +config_hash: 5ef02e55671aae1ba9bd62fe4eb0f50f From 05e3755b8fd8f03adca94eb6797c0c21b564fa80 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 20:38:34 +0000 Subject: [PATCH 009/408] codegen metadata --- .stats.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 295b77b5af..b82cec4eb6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2d116cda53321baa3479e628512def723207a81eb1cdaebb542bd0555e563bda.yml -openapi_spec_hash: 809d958fec261a32004a4b026b718793 -config_hash: 5ef02e55671aae1ba9bd62fe4eb0f50f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-de3e91790d0b9f3ce26d679ac07079880ccc695bd8c878f961c4d577a5025a2e.yml +openapi_spec_hash: 4b44e3f287583d01fbe7b10cd943254a +config_hash: 06b9a88561844d60d8efa4eaabf5fa3c From 1c0b4642054544af92c0c3a8cdf5ef3c3f62f1d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 20:39:01 +0000 Subject: [PATCH 010/408] release: 1.95.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9a75280778..ffcd85673c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.95.0" + ".": "1.95.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f5c49d637f..14d61de1bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.95.1 (2025-07-11) + +Full Changelog: [v1.95.0...v1.95.1](https://github.com/openai/openai-python/compare/v1.95.0...v1.95.1) + +### Bug Fixes + +* **client:** don't send Content-Type header on GET requests ([182b763](https://github.com/openai/openai-python/commit/182b763065fbaaf68491a7e4a15fcb23cac361de)) + ## 1.95.0 (2025-07-10) Full Changelog: [v1.94.0...v1.95.0](https://github.com/openai/openai-python/compare/v1.94.0...v1.95.0) diff --git a/pyproject.toml b/pyproject.toml index f423907080..d9305c5469 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.95.0" +version = "1.95.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 342202129c..6e2b83bbaa 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.95.0" # x-release-please-version +__version__ = "1.95.1" # x-release-please-version From 2028ad2b95f3e8f7736d45d730c0cc53852c392c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:29:56 +0000 Subject: [PATCH 011/408] feat: clean up environment call outs --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index d09de14f3c..d4b8d8d170 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,6 @@ pip install openai[aiohttp] Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: ```python -import os import asyncio from openai import DefaultAioHttpClient from openai import AsyncOpenAI @@ -168,7 +167,7 @@ from openai import AsyncOpenAI async def main() -> None: async with AsyncOpenAI( - api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted + api_key="My API Key", http_client=DefaultAioHttpClient(), ) as client: chat_completion = await client.chat.completions.create( From 1cb2bf6e0afa3d4c52c0f4d5e2ffeccaa7339624 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 13:48:50 +0000 Subject: [PATCH 012/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index b82cec4eb6..a146676471 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-de3e91790d0b9f3ce26d679ac07079880ccc695bd8c878f961c4d577a5025a2e.yml openapi_spec_hash: 4b44e3f287583d01fbe7b10cd943254a -config_hash: 06b9a88561844d60d8efa4eaabf5fa3c +config_hash: cc92d0be2a0f3c77bfc988082dd0573e From 34a565164878d97d13fb2d3f7b5602fe73ad332d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:46:45 +0000 Subject: [PATCH 013/408] chore(api): update realtime specs, build config --- .stats.yml | 6 ++--- .../types/beta/realtime/conversation_item.py | 4 ++-- .../conversation_item_created_event.py | 12 ++++++---- .../beta/realtime/conversation_item_param.py | 4 ++-- .../conversation_item_with_reference.py | 4 ++-- .../conversation_item_with_reference_param.py | 4 ++-- .../input_audio_buffer_committed_event.py | 10 +++++--- .../types/beta/realtime/realtime_response.py | 4 ++-- src/openai/types/eval_create_params.py | 23 +++++++++++++++++-- ...create_eval_completions_run_data_source.py | 23 +++++++++++++++++-- ..._eval_completions_run_data_source_param.py | 23 +++++++++++++++++-- src/openai/types/evals/run_cancel_response.py | 23 +++++++++++++++++-- src/openai/types/evals/run_create_params.py | 21 ++++++++++++++++- src/openai/types/evals/run_create_response.py | 23 +++++++++++++++++-- src/openai/types/evals/run_list_response.py | 23 +++++++++++++++++-- .../types/evals/run_retrieve_response.py | 23 +++++++++++++++++-- .../types/graders/label_model_grader.py | 20 +++++++++++++--- .../types/graders/label_model_grader_param.py | 22 +++++++++++++++--- .../types/graders/score_model_grader.py | 20 +++++++++++++--- .../types/graders/score_model_grader_param.py | 22 +++++++++++++++--- 20 files changed, 266 insertions(+), 48 deletions(-) diff --git a/.stats.yml b/.stats.yml index a146676471..12a179baf6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-de3e91790d0b9f3ce26d679ac07079880ccc695bd8c878f961c4d577a5025a2e.yml -openapi_spec_hash: 4b44e3f287583d01fbe7b10cd943254a -config_hash: cc92d0be2a0f3c77bfc988082dd0573e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-82fd6fcb3eea81cbbe09a6f831c82219f1251e1b76474b4c41f424bf277e6a71.yml +openapi_spec_hash: c8d54bd1ae3d704f6b6f72ffd2f876d8 +config_hash: 3315d58b60faf63b1bee251b81837cda diff --git a/src/openai/types/beta/realtime/conversation_item.py b/src/openai/types/beta/realtime/conversation_item.py index 4edf6c4d5f..21b7a8ac1f 100644 --- a/src/openai/types/beta/realtime/conversation_item.py +++ b/src/openai/types/beta/realtime/conversation_item.py @@ -50,8 +50,8 @@ class ConversationItem(BaseModel): for `message` items. """ - status: Optional[Literal["completed", "incomplete"]] = None - """The status of the item (`completed`, `incomplete`). + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect on the conversation, but are accepted for consistency with the `conversation.item.created` event. diff --git a/src/openai/types/beta/realtime/conversation_item_created_event.py b/src/openai/types/beta/realtime/conversation_item_created_event.py index 2f20388246..aea7ad5b4b 100644 --- a/src/openai/types/beta/realtime/conversation_item_created_event.py +++ b/src/openai/types/beta/realtime/conversation_item_created_event.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ...._models import BaseModel @@ -15,11 +16,12 @@ class ConversationItemCreatedEvent(BaseModel): item: ConversationItem """The item to add to the conversation.""" - previous_item_id: str + type: Literal["conversation.item.created"] + """The event type, must be `conversation.item.created`.""" + + previous_item_id: Optional[str] = None """ The ID of the preceding item in the Conversation context, allows the client to - understand the order of the conversation. + understand the order of the conversation. Can be `null` if the item has no + predecessor. """ - - type: Literal["conversation.item.created"] - """The event type, must be `conversation.item.created`.""" diff --git a/src/openai/types/beta/realtime/conversation_item_param.py b/src/openai/types/beta/realtime/conversation_item_param.py index ac0f8431e5..8bbd539c0c 100644 --- a/src/openai/types/beta/realtime/conversation_item_param.py +++ b/src/openai/types/beta/realtime/conversation_item_param.py @@ -51,8 +51,8 @@ class ConversationItemParam(TypedDict, total=False): for `message` items. """ - status: Literal["completed", "incomplete"] - """The status of the item (`completed`, `incomplete`). + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect on the conversation, but are accepted for consistency with the `conversation.item.created` event. diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference.py b/src/openai/types/beta/realtime/conversation_item_with_reference.py index 31806afc33..dec7a5a409 100644 --- a/src/openai/types/beta/realtime/conversation_item_with_reference.py +++ b/src/openai/types/beta/realtime/conversation_item_with_reference.py @@ -53,8 +53,8 @@ class ConversationItemWithReference(BaseModel): for `message` items. """ - status: Optional[Literal["completed", "incomplete"]] = None - """The status of the item (`completed`, `incomplete`). + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect on the conversation, but are accepted for consistency with the `conversation.item.created` event. diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference_param.py b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py index e266cdce32..3778373a4c 100644 --- a/src/openai/types/beta/realtime/conversation_item_with_reference_param.py +++ b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py @@ -54,8 +54,8 @@ class ConversationItemWithReferenceParam(TypedDict, total=False): for `message` items. """ - status: Literal["completed", "incomplete"] - """The status of the item (`completed`, `incomplete`). + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item (`completed`, `incomplete`, `in_progress`). These have no effect on the conversation, but are accepted for consistency with the `conversation.item.created` event. diff --git a/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py b/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py index 3071eff357..22eb53b117 100644 --- a/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py +++ b/src/openai/types/beta/realtime/input_audio_buffer_committed_event.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ...._models import BaseModel @@ -14,8 +15,11 @@ class InputAudioBufferCommittedEvent(BaseModel): item_id: str """The ID of the user message item that will be created.""" - previous_item_id: str - """The ID of the preceding item after which the new item will be inserted.""" - type: Literal["input_audio_buffer.committed"] """The event type, must be `input_audio_buffer.committed`.""" + + previous_item_id: Optional[str] = None + """ + The ID of the preceding item after which the new item will be inserted. Can be + `null` if the item has no predecessor. + """ diff --git a/src/openai/types/beta/realtime/realtime_response.py b/src/openai/types/beta/realtime/realtime_response.py index 8ecfb91c31..28e03c8717 100644 --- a/src/openai/types/beta/realtime/realtime_response.py +++ b/src/openai/types/beta/realtime/realtime_response.py @@ -60,10 +60,10 @@ class RealtimeResponse(BaseModel): output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" - status: Optional[Literal["completed", "cancelled", "failed", "incomplete"]] = None + status: Optional[Literal["completed", "cancelled", "failed", "incomplete", "in_progress"]] = None """ The final status of the response (`completed`, `cancelled`, `failed`, or - `incomplete`). + `incomplete`, `in_progress`). """ status_details: Optional[RealtimeResponseStatus] = None diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index 20a3765481..9674785701 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -25,6 +25,7 @@ "TestingCriterionLabelModelInputEvalItem", "TestingCriterionLabelModelInputEvalItemContent", "TestingCriterionLabelModelInputEvalItemContentOutputText", + "TestingCriterionLabelModelInputEvalItemContentInputImage", "TestingCriterionTextSimilarity", "TestingCriterionPython", "TestingCriterionScoreModel", @@ -109,14 +110,32 @@ class TestingCriterionLabelModelInputEvalItemContentOutputText(TypedDict, total= """The type of the output text. Always `output_text`.""" +class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total=False): + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + TestingCriterionLabelModelInputEvalItemContent: TypeAlias = Union[ - str, ResponseInputTextParam, TestingCriterionLabelModelInputEvalItemContentOutputText + str, + ResponseInputTextParam, + TestingCriterionLabelModelInputEvalItemContentOutputText, + TestingCriterionLabelModelInputEvalItemContentInputImage, + Iterable[object], ] class TestingCriterionLabelModelInputEvalItem(TypedDict, total=False): content: Required[TestingCriterionLabelModelInputEvalItemContent] - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 0a942cd200..a0eaa5addb 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -26,6 +26,7 @@ "InputMessagesTemplateTemplateMessage", "InputMessagesTemplateTemplateMessageContent", "InputMessagesTemplateTemplateMessageContentOutputText", + "InputMessagesTemplateTemplateMessageContentInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -94,14 +95,32 @@ class InputMessagesTemplateTemplateMessageContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" +class InputMessagesTemplateTemplateMessageContentInputImage(BaseModel): + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + InputMessagesTemplateTemplateMessageContent: TypeAlias = Union[ - str, ResponseInputText, InputMessagesTemplateTemplateMessageContentOutputText + str, + ResponseInputText, + InputMessagesTemplateTemplateMessageContentOutputText, + InputMessagesTemplateTemplateMessageContentInputImage, + List[object], ] class InputMessagesTemplateTemplateMessage(BaseModel): content: InputMessagesTemplateTemplateMessageContent - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 84344fcd94..8892b68b17 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -26,6 +26,7 @@ "InputMessagesTemplateTemplateMessage", "InputMessagesTemplateTemplateMessageContent", "InputMessagesTemplateTemplateMessageContentOutputText", + "InputMessagesTemplateTemplateMessageContentInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -92,14 +93,32 @@ class InputMessagesTemplateTemplateMessageContentOutputText(TypedDict, total=Fal """The type of the output text. Always `output_text`.""" +class InputMessagesTemplateTemplateMessageContentInputImage(TypedDict, total=False): + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + InputMessagesTemplateTemplateMessageContent: TypeAlias = Union[ - str, ResponseInputTextParam, InputMessagesTemplateTemplateMessageContentOutputText + str, + ResponseInputTextParam, + InputMessagesTemplateTemplateMessageContentOutputText, + InputMessagesTemplateTemplateMessageContentInputImage, + Iterable[object], ] class InputMessagesTemplateTemplateMessage(TypedDict, total=False): content: Required[InputMessagesTemplateTemplateMessageContent] - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 12cc868045..7f4f4c9cc4 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -32,6 +32,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -138,14 +139,32 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ - str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + List[object], ] class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index 354a81132e..1622b00eb7 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -29,6 +29,7 @@ "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference", "DataSourceCreateEvalResponsesRunDataSourceSamplingParams", "DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText", @@ -153,16 +154,34 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva """The type of the output text. Always `output_text`.""" +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage( + TypedDict, total=False +): + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage, + Iterable[object], ] class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem(TypedDict, total=False): content: Required[DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent] - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 776ebb413f..fba5321552 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -32,6 +32,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -138,14 +139,32 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ - str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + List[object], ] class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index 9e2374f93c..e9e445af5c 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -32,6 +32,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -138,14 +139,32 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ - str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + List[object], ] class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index a4f43ce3f9..e13f1abe42 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -32,6 +32,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -138,14 +139,32 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ - str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + List[object], ] class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/graders/label_model_grader.py b/src/openai/types/graders/label_model_grader.py index d95ccc6df6..76dbfb854a 100644 --- a/src/openai/types/graders/label_model_grader.py +++ b/src/openai/types/graders/label_model_grader.py @@ -6,7 +6,7 @@ from ..._models import BaseModel from ..responses.response_input_text import ResponseInputText -__all__ = ["LabelModelGrader", "Input", "InputContent", "InputContentOutputText"] +__all__ = ["LabelModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] class InputContentOutputText(BaseModel): @@ -17,12 +17,26 @@ class InputContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText] +class InputContentInputImage(BaseModel): + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText, InputContentInputImage, List[object]] class Input(BaseModel): content: InputContent - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py index 76d01421ee..941c8a1bd0 100644 --- a/src/openai/types/graders/label_model_grader_param.py +++ b/src/openai/types/graders/label_model_grader_param.py @@ -7,7 +7,7 @@ from ..responses.response_input_text_param import ResponseInputTextParam -__all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText"] +__all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] class InputContentOutputText(TypedDict, total=False): @@ -18,12 +18,28 @@ class InputContentOutputText(TypedDict, total=False): """The type of the output text. Always `output_text`.""" -InputContent: TypeAlias = Union[str, ResponseInputTextParam, InputContentOutputText] +class InputContentInputImage(TypedDict, total=False): + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[ + str, ResponseInputTextParam, InputContentOutputText, InputContentInputImage, Iterable[object] +] class Input(TypedDict, total=False): content: Required[InputContent] - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index 1349f75a58..e6af0ebcf7 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -6,7 +6,7 @@ from ..._models import BaseModel from ..responses.response_input_text import ResponseInputText -__all__ = ["ScoreModelGrader", "Input", "InputContent", "InputContentOutputText"] +__all__ = ["ScoreModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] class InputContentOutputText(BaseModel): @@ -17,12 +17,26 @@ class InputContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText] +class InputContentInputImage(BaseModel): + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText, InputContentInputImage, List[object]] class Input(BaseModel): content: InputContent - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 673f14e47d..47c9928076 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -7,7 +7,7 @@ from ..responses.response_input_text_param import ResponseInputTextParam -__all__ = ["ScoreModelGraderParam", "Input", "InputContent", "InputContentOutputText"] +__all__ = ["ScoreModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] class InputContentOutputText(TypedDict, total=False): @@ -18,12 +18,28 @@ class InputContentOutputText(TypedDict, total=False): """The type of the output text. Always `output_text`.""" -InputContent: TypeAlias = Union[str, ResponseInputTextParam, InputContentOutputText] +class InputContentInputImage(TypedDict, total=False): + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContent: TypeAlias = Union[ + str, ResponseInputTextParam, InputContentOutputText, InputContentInputImage, Iterable[object] +] class Input(TypedDict, total=False): content: Required[InputContent] - """Text inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings.""" role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. From 1d77265e3d31afda8df6528a1926c854ef27de3b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:47:15 +0000 Subject: [PATCH 014/408] release: 1.96.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ffcd85673c..db912a0d0f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.95.1" + ".": "1.96.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 14d61de1bf..c91c4c4b35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.96.0 (2025-07-15) + +Full Changelog: [v1.95.1...v1.96.0](https://github.com/openai/openai-python/compare/v1.95.1...v1.96.0) + +### Features + +* clean up environment call outs ([87c2e97](https://github.com/openai/openai-python/commit/87c2e979e0ec37347b7f595c2696408acd25fe20)) + + +### Chores + +* **api:** update realtime specs, build config ([bf06d88](https://github.com/openai/openai-python/commit/bf06d88b33f9af82a51d9a8af5b7a38925906f7a)) + ## 1.95.1 (2025-07-11) Full Changelog: [v1.95.0...v1.95.1](https://github.com/openai/openai-python/compare/v1.95.0...v1.95.1) diff --git a/pyproject.toml b/pyproject.toml index d9305c5469..65055d926a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.95.1" +version = "1.96.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6e2b83bbaa..b1025f4a31 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.95.1" # x-release-please-version +__version__ = "1.96.0" # x-release-please-version From 7bbb31cba0b056a191277a63e9798ffc4c3f7586 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:20:27 +0000 Subject: [PATCH 015/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 12a179baf6..7d1cdd14ad 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-82fd6fcb3eea81cbbe09a6f831c82219f1251e1b76474b4c41f424bf277e6a71.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-79dcb0ae501ac17004f50aecb112a798290ab3727fbe7c7d1b34299e38ed4f8e.yml openapi_spec_hash: c8d54bd1ae3d704f6b6f72ffd2f876d8 -config_hash: 3315d58b60faf63b1bee251b81837cda +config_hash: 167ad0ca036d0f023c78e6496b4311e8 From 3876ddc28e833aca190d6ec8eaf3b42c979f6e99 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 21:27:39 +0000 Subject: [PATCH 016/408] chore(api): update realtime specs --- .stats.yml | 4 +-- .../realtime/conversation_item_content.py | 9 ++++--- .../conversation_item_content_param.py | 9 ++++--- .../conversation_item_with_reference.py | 26 ++++++++++++++++--- .../conversation_item_with_reference_param.py | 25 +++++++++++++++--- 5 files changed, 59 insertions(+), 14 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7d1cdd14ad..571b0ee797 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-79dcb0ae501ac17004f50aecb112a798290ab3727fbe7c7d1b34299e38ed4f8e.yml -openapi_spec_hash: c8d54bd1ae3d704f6b6f72ffd2f876d8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c7dacca97e28bceff218684bb429481a70aa47aadad983ed9178bfda75ff4cd2.yml +openapi_spec_hash: 28eb1bb901ca10d2e37db4606d2bcfa7 config_hash: 167ad0ca036d0f023c78e6496b4311e8 diff --git a/src/openai/types/beta/realtime/conversation_item_content.py b/src/openai/types/beta/realtime/conversation_item_content.py index ab40a4a1a7..fe9cef80e3 100644 --- a/src/openai/types/beta/realtime/conversation_item_content.py +++ b/src/openai/types/beta/realtime/conversation_item_content.py @@ -23,7 +23,10 @@ class ConversationItemContent(BaseModel): """The text content, used for `input_text` and `text` content types.""" transcript: Optional[str] = None - """The transcript of the audio, used for `input_audio` content type.""" + """The transcript of the audio, used for `input_audio` and `audio` content types.""" - type: Optional[Literal["input_text", "input_audio", "item_reference", "text"]] = None - """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" + type: Optional[Literal["input_text", "input_audio", "item_reference", "text", "audio"]] = None + """ + The content type (`input_text`, `input_audio`, `item_reference`, `text`, + `audio`). + """ diff --git a/src/openai/types/beta/realtime/conversation_item_content_param.py b/src/openai/types/beta/realtime/conversation_item_content_param.py index 7a3a92a39d..6042e7f90f 100644 --- a/src/openai/types/beta/realtime/conversation_item_content_param.py +++ b/src/openai/types/beta/realtime/conversation_item_content_param.py @@ -22,7 +22,10 @@ class ConversationItemContentParam(TypedDict, total=False): """The text content, used for `input_text` and `text` content types.""" transcript: str - """The transcript of the audio, used for `input_audio` content type.""" + """The transcript of the audio, used for `input_audio` and `audio` content types.""" - type: Literal["input_text", "input_audio", "item_reference", "text"] - """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" + type: Literal["input_text", "input_audio", "item_reference", "text", "audio"] + """ + The content type (`input_text`, `input_audio`, `item_reference`, `text`, + `audio`). + """ diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference.py b/src/openai/types/beta/realtime/conversation_item_with_reference.py index dec7a5a409..0edcfc76b6 100644 --- a/src/openai/types/beta/realtime/conversation_item_with_reference.py +++ b/src/openai/types/beta/realtime/conversation_item_with_reference.py @@ -4,9 +4,29 @@ from typing_extensions import Literal from ...._models import BaseModel -from .conversation_item_content import ConversationItemContent -__all__ = ["ConversationItemWithReference"] +__all__ = ["ConversationItemWithReference", "Content"] + + +class Content(BaseModel): + id: Optional[str] = None + """ + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. + """ + + audio: Optional[str] = None + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: Optional[str] = None + """The text content, used for `input_text` and `text` content types.""" + + transcript: Optional[str] = None + """The transcript of the audio, used for `input_audio` content type.""" + + type: Optional[Literal["input_text", "input_audio", "item_reference", "text"]] = None + """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" class ConversationItemWithReference(BaseModel): @@ -30,7 +50,7 @@ class ConversationItemWithReference(BaseModel): `function_call` item with the same ID exists in the conversation history. """ - content: Optional[List[ConversationItemContent]] = None + content: Optional[List[Content]] = None """The content of the message, applicable for `message` items. - Message items of role `system` support only `input_text` content diff --git a/src/openai/types/beta/realtime/conversation_item_with_reference_param.py b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py index 3778373a4c..c83dc92ab7 100644 --- a/src/openai/types/beta/realtime/conversation_item_with_reference_param.py +++ b/src/openai/types/beta/realtime/conversation_item_with_reference_param.py @@ -5,9 +5,28 @@ from typing import Iterable from typing_extensions import Literal, TypedDict -from .conversation_item_content_param import ConversationItemContentParam +__all__ = ["ConversationItemWithReferenceParam", "Content"] -__all__ = ["ConversationItemWithReferenceParam"] + +class Content(TypedDict, total=False): + id: str + """ + ID of a previous conversation item to reference (for `item_reference` content + types in `response.create` events). These can reference both client and server + created items. + """ + + audio: str + """Base64-encoded audio bytes, used for `input_audio` content type.""" + + text: str + """The text content, used for `input_text` and `text` content types.""" + + transcript: str + """The transcript of the audio, used for `input_audio` content type.""" + + type: Literal["input_text", "input_audio", "item_reference", "text"] + """The content type (`input_text`, `input_audio`, `item_reference`, `text`).""" class ConversationItemWithReferenceParam(TypedDict, total=False): @@ -31,7 +50,7 @@ class ConversationItemWithReferenceParam(TypedDict, total=False): `function_call` item with the same ID exists in the conversation history. """ - content: Iterable[ConversationItemContentParam] + content: Iterable[Content] """The content of the message, applicable for `message` items. - Message items of role `system` support only `input_text` content From 859b4db4a7b3c229cd4c19eb21642faca007530b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 21:28:05 +0000 Subject: [PATCH 017/408] release: 1.96.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index db912a0d0f..6b38a1bd5a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.96.0" + ".": "1.96.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c91c4c4b35..93bfb63f37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.96.1 (2025-07-15) + +Full Changelog: [v1.96.0...v1.96.1](https://github.com/openai/openai-python/compare/v1.96.0...v1.96.1) + +### Chores + +* **api:** update realtime specs ([b68b71b](https://github.com/openai/openai-python/commit/b68b71b178719e0b49ecfe34486b9d9ac0627924)) + ## 1.96.0 (2025-07-15) Full Changelog: [v1.95.1...v1.96.0](https://github.com/openai/openai-python/compare/v1.95.1...v1.96.0) diff --git a/pyproject.toml b/pyproject.toml index 65055d926a..0f655d058d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.96.0" +version = "1.96.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b1025f4a31..39be0338f6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.96.0" # x-release-please-version +__version__ = "1.96.1" # x-release-please-version From a85ad051aa4e6cf4f81a51714afc7bc90310e047 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 16:24:53 +0000 Subject: [PATCH 018/408] feat(api): manual updates --- .stats.yml | 6 +- api.md | 12 +- examples/image_stream.py | 53 + src/openai/_streaming.py | 7 +- src/openai/resources/images.py | 1453 ++++++++++++++--- src/openai/types/__init__.py | 6 + .../types/image_edit_completed_event.py | 55 + src/openai/types/image_edit_params.py | 42 +- .../types/image_edit_partial_image_event.py | 33 + src/openai/types/image_edit_stream_event.py | 14 + src/openai/types/image_gen_completed_event.py | 55 + .../types/image_gen_partial_image_event.py | 33 + src/openai/types/image_gen_stream_event.py | 14 + src/openai/types/image_generate_params.py | 35 +- .../responses/response_output_refusal.py | 2 +- .../response_output_refusal_param.py | 2 +- src/openai/types/responses/tool.py | 7 + src/openai/types/responses/tool_param.py | 7 + tests/api_resources/test_images.py | 262 ++- 19 files changed, 1880 insertions(+), 218 deletions(-) create mode 100644 examples/image_stream.py create mode 100644 src/openai/types/image_edit_completed_event.py create mode 100644 src/openai/types/image_edit_partial_image_event.py create mode 100644 src/openai/types/image_edit_stream_event.py create mode 100644 src/openai/types/image_gen_completed_event.py create mode 100644 src/openai/types/image_gen_partial_image_event.py create mode 100644 src/openai/types/image_gen_stream_event.py diff --git a/.stats.yml b/.stats.yml index 571b0ee797..2b9160cf6e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c7dacca97e28bceff218684bb429481a70aa47aadad983ed9178bfda75ff4cd2.yml -openapi_spec_hash: 28eb1bb901ca10d2e37db4606d2bcfa7 -config_hash: 167ad0ca036d0f023c78e6496b4311e8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-670ea0d2cc44f52a87dd3cadea45632953283e0636ba30788fdbdb22a232ccac.yml +openapi_spec_hash: d8b7d38911fead545adf3e4297956410 +config_hash: 5525bda35e48ea6387c6175c4d1651fa diff --git a/api.md b/api.md index abf0de481d..b3a2245cdd 100644 --- a/api.md +++ b/api.md @@ -127,7 +127,17 @@ Methods: Types: ```python -from openai.types import Image, ImageModel, ImagesResponse +from openai.types import ( + Image, + ImageEditCompletedEvent, + ImageEditPartialImageEvent, + ImageEditStreamEvent, + ImageGenCompletedEvent, + ImageGenPartialImageEvent, + ImageGenStreamEvent, + ImageModel, + ImagesResponse, +) ``` Methods: diff --git a/examples/image_stream.py b/examples/image_stream.py new file mode 100644 index 0000000000..c188e68717 --- /dev/null +++ b/examples/image_stream.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +import base64 +from pathlib import Path + +from openai import OpenAI + +client = OpenAI() + + +def main() -> None: + """Example of OpenAI image streaming with partial images.""" + stream = client.images.generate( + model="gpt-image-1", + prompt="A cute baby sea otter", + n=1, + size="1024x1024", + stream=True, + partial_images=3, + ) + + for event in stream: + if event.type == "image_generation.partial_image": + print(f" Partial image {event.partial_image_index + 1}/3 received") + print(f" Size: {len(event.b64_json)} characters (base64)") + + # Save partial image to file + filename = f"partial_{event.partial_image_index + 1}.png" + image_data = base64.b64decode(event.b64_json) + with open(filename, "wb") as f: + f.write(image_data) + print(f" 💾 Saved to: {Path(filename).resolve()}") + + elif event.type == "image_generation.completed": + print(f"\n✅ Final image completed!") + print(f" Size: {len(event.b64_json)} characters (base64)") + + # Save final image to file + filename = "final_image.png" + image_data = base64.b64decode(event.b64_json) + with open(filename, "wb") as f: + f.write(image_data) + print(f" 💾 Saved to: {Path(filename).resolve()}") + + else: + print(f"❓ Unknown event: {event}") # type: ignore[unreachable] + + +if __name__ == "__main__": + try: + main() + except Exception as error: + print(f"Error generating image: {error}") \ No newline at end of file diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index f5621f92a7..fa0a30e183 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -59,7 +59,12 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None or sse.event.startswith("response.") or sse.event.startswith("transcript."): + if sse.event is None or ( + sse.event.startswith("response.") or + sse.event.startswith("transcript.") or + sse.event.startswith("image_edit.") or + sse.event.startswith("image_generation.") + ): data = sse.json() if is_mapping(data) and data.get("error"): message = None diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 43f6189f91..77b7a1b24e 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -3,20 +3,23 @@ from __future__ import annotations from typing import List, Union, Mapping, Optional, cast -from typing_extensions import Literal +from typing_extensions import Literal, overload import httpx from .. import _legacy_response from ..types import image_edit_params, image_generate_params, image_create_variation_params from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .._streaming import Stream, AsyncStream from .._base_client import make_request_options from ..types.image_model import ImageModel from ..types.images_response import ImagesResponse +from ..types.image_gen_stream_event import ImageGenStreamEvent +from ..types.image_edit_stream_event import ImageEditStreamEvent __all__ = ["Images", "AsyncImages"] @@ -114,21 +117,25 @@ def create_variation( cast_to=ImagesResponse, ) + @overload def edit( self, *, image: Union[FileTypes, List[FileTypes]], prompt: str, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, mask: FileTypes | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, output_compression: Optional[int] | NotGiven = NOT_GIVEN, output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -162,6 +169,234 @@ def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are + supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` + is used. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. `high`, `medium` and `low` are + only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. + Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` + will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def edit( + self, + *, + image: Union[FileTypes, List[FileTypes]], + prompt: str, + stream: Literal[True], + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, + mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint only supports `gpt-image-1` and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 50MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are + supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` + is used. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. `high`, `medium` and `low` are + only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. + Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` + will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def edit( + self, + *, + image: Union[FileTypes, List[FileTypes]], + prompt: str, + stream: bool, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, + mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse | Stream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint only supports `gpt-image-1` and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 50MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, the mask will be applied on the first image. Must be a valid PNG file, less than @@ -181,6 +416,10 @@ def edit( supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -206,19 +445,51 @@ def edit( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @required_args(["image", "prompt"], ["image", "prompt", "stream"]) + def edit( + self, + *, + image: Union[FileTypes, List[FileTypes]], + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, + mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse | Stream[ImageEditStreamEvent]: body = deepcopy_minimal( { "image": image, "prompt": prompt, "background": background, + "input_fidelity": input_fidelity, "mask": mask, "model": model, "n": n, "output_compression": output_compression, "output_format": output_format, + "partial_images": partial_images, "quality": quality, "response_format": response_format, "size": size, + "stream": stream, "user": user, } ) @@ -229,15 +500,891 @@ def edit( extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/images/edits", - body=maybe_transform(body, image_edit_params.ImageEditParams), + body=maybe_transform( + body, + image_edit_params.ImageEditParamsStreaming if stream else image_edit_params.ImageEditParamsNonStreaming, + ), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + stream=stream or False, + stream_cls=Stream[ImageEditStreamEvent], + ) + + @overload + def generate( + self, + *, + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] + | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """ + Creates an image given a prompt. + [Learn more](https://platform.openai.com/docs/guides/images). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters + for `dall-e-3`. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or + `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to + `gpt-image-1` is used. + + moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must + be either `low` for less restrictive filtering or `auto` (default value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for + `gpt-image-1` which will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and + one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def generate( + self, + *, + prompt: str, + stream: Literal[True], + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] + | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Stream[ImageGenStreamEvent]: + """ + Creates an image given a prompt. + [Learn more](https://platform.openai.com/docs/guides/images). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters + for `dall-e-3`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or + `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to + `gpt-image-1` is used. + + moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must + be either `low` for less restrictive filtering or `auto` (default value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for + `gpt-image-1` which will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and + one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def generate( + self, + *, + prompt: str, + stream: bool, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] + | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse | Stream[ImageGenStreamEvent]: + """ + Creates an image given a prompt. + [Learn more](https://platform.openai.com/docs/guides/images). + + Args: + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters + for `dall-e-3`. + + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or + `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to + `gpt-image-1` is used. + + moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must + be either `low` for less restrictive filtering or `auto` (default value). + + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for + `gpt-image-1` which will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and + one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["prompt"], ["prompt", "stream"]) + def generate( + self, + *, + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] + | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse | Stream[ImageGenStreamEvent]: + return self._post( + "/images/generations", + body=maybe_transform( + { + "prompt": prompt, + "background": background, + "model": model, + "moderation": moderation, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "response_format": response_format, + "size": size, + "stream": stream, + "style": style, + "user": user, + }, + image_generate_params.ImageGenerateParamsStreaming + if stream + else image_generate_params.ImageGenerateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + stream=stream or False, + stream_cls=Stream[ImageGenStreamEvent], + ) + + +class AsyncImages(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncImagesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncImagesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncImagesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncImagesWithStreamingResponse(self) + + async def create_variation( + self, + *, + image: FileTypes, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """Creates a variation of a given image. + + This endpoint only supports `dall-e-2`. + + Args: + image: The image to use as the basis for the variation(s). Must be a valid PNG file, + less than 4MB, and square. + + model: The model to use for image generation. Only `dall-e-2` is supported at this + time. + + n: The number of images to generate. Must be between 1 and 10. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. + + size: The size of the generated images. Must be one of `256x256`, `512x512`, or + `1024x1024`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "image": image, + "model": model, + "n": n, + "response_format": response_format, + "size": size, + "user": user, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/images/variations", + body=await async_maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ImagesResponse, + ) + + @overload + async def edit( + self, + *, + image: Union[FileTypes, List[FileTypes]], + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, + mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint only supports `gpt-image-1` and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 50MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are + supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` + is used. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. `high`, `medium` and `low` are + only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. + Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` + will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def edit( + self, + *, + image: Union[FileTypes, List[FileTypes]], + prompt: str, + stream: Literal[True], + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, + mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncStream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint only supports `gpt-image-1` and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 50MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are + supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` + is used. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. `high`, `medium` and `low` are + only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. + Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` + will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def edit( + self, + *, + image: Union[FileTypes, List[FileTypes]], + prompt: str, + stream: bool, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, + mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]: + """Creates an edited or extended image given one or more source images and a + prompt. + + This endpoint only supports `gpt-image-1` and `dall-e-2`. + + Args: + image: The image(s) to edit. Must be a supported image file or an array of images. + + For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + 50MB. You can provide up to 16 images. + + For `dall-e-2`, you can only provide one image, and it should be a square `png` + file less than 4MB. + + prompt: A text description of the desired image(s). The maximum length is 1000 + characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + + stream: Edit the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + + background: Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + + mask: An additional image whose fully transparent areas (e.g. where alpha is zero) + indicate where `image` should be edited. If there are multiple images provided, + the mask will be applied on the first image. Must be a valid PNG file, less than + 4MB, and have the same dimensions as `image`. + + model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are + supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` + is used. + + n: The number of images to generate. Must be between 1 and 10. + + output_compression: The compression level (0-100%) for the generated images. This parameter is only + supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + defaults to 100. + + output_format: The format in which the generated images are returned. This parameter is only + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + default value is `png`. + + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + + quality: The quality of the image that will be generated. `high`, `medium` and `low` are + only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. + Defaults to `auto`. + + response_format: The format in which the generated images are returned. Must be one of `url` or + `b64_json`. URLs are only valid for 60 minutes after the image has been + generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` + will always return base64-encoded images. + + size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` + (landscape), `1024x1536` (portrait), or `auto` (default value) for + `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + + user: A unique identifier representing your end-user, which can help OpenAI to monitor + and detect abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["image", "prompt"], ["image", "prompt", "stream"]) + async def edit( + self, + *, + image: Union[FileTypes, List[FileTypes]], + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, + mask: FileTypes | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]: + body = deepcopy_minimal( + { + "image": image, + "prompt": prompt, + "background": background, + "input_fidelity": input_fidelity, + "mask": mask, + "model": model, + "n": n, + "output_compression": output_compression, + "output_format": output_format, + "partial_images": partial_images, + "quality": quality, + "response_format": response_format, + "size": size, + "stream": stream, + "user": user, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/images/edits", + body=await async_maybe_transform( + body, + image_edit_params.ImageEditParamsStreaming if stream else image_edit_params.ImageEditParamsNonStreaming, + ), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ImagesResponse, + stream=stream or False, + stream_cls=AsyncStream[ImageEditStreamEvent], ) - def generate( + @overload + async def generate( self, *, prompt: str, @@ -247,12 +1394,14 @@ def generate( n: Optional[int] | NotGiven = NOT_GIVEN, output_compression: Optional[int] | NotGiven = NOT_GIVEN, output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -296,6 +1445,10 @@ def generate( output_format: The format in which the generated images are returned. This parameter is only supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the @@ -314,6 +1467,10 @@ def generate( `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. + style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean towards generating hyper-real and dramatic images. Natural causes the model to @@ -331,140 +1488,28 @@ def generate( timeout: Override the client-level default timeout for this request, in seconds """ - return self._post( - "/images/generations", - body=maybe_transform( - { - "prompt": prompt, - "background": background, - "model": model, - "moderation": moderation, - "n": n, - "output_compression": output_compression, - "output_format": output_format, - "quality": quality, - "response_format": response_format, - "size": size, - "style": style, - "user": user, - }, - image_generate_params.ImageGenerateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ImagesResponse, - ) - - -class AsyncImages(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncImagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers - """ - return AsyncImagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncImagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/openai/openai-python#with_streaming_response - """ - return AsyncImagesWithStreamingResponse(self) - - async def create_variation( - self, - *, - image: FileTypes, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ImagesResponse: - """Creates a variation of a given image. - - This endpoint only supports `dall-e-2`. - - Args: - image: The image to use as the basis for the variation(s). Must be a valid PNG file, - less than 4MB, and square. - - model: The model to use for image generation. Only `dall-e-2` is supported at this - time. - - n: The number of images to generate. Must be between 1 and 10. - - response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. - - size: The size of the generated images. Must be one of `256x256`, `512x512`, or - `1024x1024`. - - user: A unique identifier representing your end-user, which can help OpenAI to monitor - and detect abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - body = deepcopy_minimal( - { - "image": image, - "model": model, - "n": n, - "response_format": response_format, - "size": size, - "user": user, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/images/variations", - body=await async_maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ImagesResponse, - ) + ... - async def edit( + @overload + async def generate( self, *, - image: Union[FileTypes, List[FileTypes]], prompt: str, + stream: Literal[True], background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, output_compression: Optional[int] | NotGiven = NOT_GIVEN, output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -472,23 +1517,19 @@ async def edit( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ImagesResponse: - """Creates an edited or extended image given one or more source images and a - prompt. - - This endpoint only supports `gpt-image-1` and `dall-e-2`. + ) -> AsyncStream[ImageGenStreamEvent]: + """ + Creates an image given a prompt. + [Learn more](https://platform.openai.com/docs/guides/images). Args: - image: The image(s) to edit. Must be a supported image file or an array of images. - - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. - - For `dall-e-2`, you can only provide one image, and it should be a square `png` - file less than 4MB. + prompt: A text description of the desired image(s). The maximum length is 32000 + characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters + for `dall-e-3`. - prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. background: Allows to set transparency for the background of the generated image(s). This parameter is only supported for `gpt-image-1`. Must be one of `transparent`, @@ -498,37 +1539,49 @@ async def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - mask: An additional image whose fully transparent areas (e.g. where alpha is zero) - indicate where `image` should be edited. If there are multiple images provided, - the mask will be applied on the first image. Must be a valid PNG file, less than - 4MB, and have the same dimensions as `image`. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or + `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to + `gpt-image-1` is used. - model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are - supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` - is used. + moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must + be either `low` for less restrictive filtering or `auto` (default value). - n: The number of images to generate. Must be between 1 and 10. + n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only + `n=1` is supported. output_compression: The compression level (0-100%) for the generated images. This parameter is only supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The - default value is `png`. + supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. - quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. - Defaults to `auto`. + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. - response_format: The format in which the generated images are returned. Must be one of `url` or - `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` - will always return base64-encoded images. + quality: The quality of the image that will be generated. + + - `auto` (default value) will automatically select the best quality for the + given model. + - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `hd` and `standard` are supported for `dall-e-3`. + - `standard` is the only option for `dall-e-2`. + + response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are + returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes + after the image has been generated. This parameter isn't supported for + `gpt-image-1` which will always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and + one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + + style: The style of the generated images. This parameter is only supported for + `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean + towards generating hyper-real and dramatic images. Natural causes the model to + produce more natural, less hyper-real looking images. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -542,47 +1595,21 @@ async def edit( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( - { - "image": image, - "prompt": prompt, - "background": background, - "mask": mask, - "model": model, - "n": n, - "output_compression": output_compression, - "output_format": output_format, - "quality": quality, - "response_format": response_format, - "size": size, - "user": user, - } - ) - files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} - return await self._post( - "/images/edits", - body=await async_maybe_transform(body, image_edit_params.ImageEditParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ImagesResponse, - ) + ... + @overload async def generate( self, *, prompt: str, + stream: bool, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, n: Optional[int] | NotGiven = NOT_GIVEN, output_compression: Optional[int] | NotGiven = NOT_GIVEN, output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, size: Optional[ @@ -597,7 +1624,7 @@ async def generate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ImagesResponse: + ) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]: """ Creates an image given a prompt. [Learn more](https://platform.openai.com/docs/guides/images). @@ -607,6 +1634,10 @@ async def generate( characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters for `dall-e-3`. + stream: Generate the image in streaming mode. Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. + background: Allows to set transparency for the background of the generated image(s). This parameter is only supported for `gpt-image-1`. Must be one of `transparent`, `opaque` or `auto` (default value). When `auto` is used, the model will @@ -632,6 +1663,10 @@ async def generate( output_format: The format in which the generated images are returned. This parameter is only supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + partial_images: The number of partial images to generate. This parameter is used for streaming + responses that return partial images. Value must be between 0 and 3. When set to + 0, the response will be a single image sent in one streaming event. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the @@ -667,6 +1702,36 @@ async def generate( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @required_args(["prompt"], ["prompt", "stream"]) + async def generate( + self, + *, + prompt: str, + background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, + moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, + n: Optional[int] | NotGiven = NOT_GIVEN, + output_compression: Optional[int] | NotGiven = NOT_GIVEN, + output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, + partial_images: Optional[int] | NotGiven = NOT_GIVEN, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, + response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + size: Optional[ + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] + ] + | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, + user: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]: return await self._post( "/images/generations", body=await async_maybe_transform( @@ -678,18 +1743,24 @@ async def generate( "n": n, "output_compression": output_compression, "output_format": output_format, + "partial_images": partial_images, "quality": quality, "response_format": response_format, "size": size, + "stream": stream, "style": style, "user": user, }, - image_generate_params.ImageGenerateParams, + image_generate_params.ImageGenerateParamsStreaming + if stream + else image_generate_params.ImageGenerateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ImagesResponse, + stream=stream or False, + stream_cls=AsyncStream[ImageGenStreamEvent], ) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 453b26f555..51f3ee5c9b 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -60,15 +60,19 @@ from .image_generate_params import ImageGenerateParams as ImageGenerateParams from .eval_retrieve_response import EvalRetrieveResponse as EvalRetrieveResponse from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy +from .image_gen_stream_event import ImageGenStreamEvent as ImageGenStreamEvent from .upload_complete_params import UploadCompleteParams as UploadCompleteParams from .container_create_params import ContainerCreateParams as ContainerCreateParams from .container_list_response import ContainerListResponse as ContainerListResponse from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams +from .image_edit_stream_event import ImageEditStreamEvent as ImageEditStreamEvent from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .moderation_create_params import ModerationCreateParams as ModerationCreateParams from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams from .container_create_response import ContainerCreateResponse as ContainerCreateResponse from .create_embedding_response import CreateEmbeddingResponse as CreateEmbeddingResponse +from .image_gen_completed_event import ImageGenCompletedEvent as ImageGenCompletedEvent +from .image_edit_completed_event import ImageEditCompletedEvent as ImageEditCompletedEvent from .moderation_create_response import ModerationCreateResponse as ModerationCreateResponse from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams from .vector_store_search_params import VectorStoreSearchParams as VectorStoreSearchParams @@ -79,8 +83,10 @@ from .vector_store_search_response import VectorStoreSearchResponse as VectorStoreSearchResponse from .websocket_connection_options import WebsocketConnectionOptions as WebsocketConnectionOptions from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams +from .image_gen_partial_image_event import ImageGenPartialImageEvent as ImageGenPartialImageEvent from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy from .eval_custom_data_source_config import EvalCustomDataSourceConfig as EvalCustomDataSourceConfig +from .image_edit_partial_image_event import ImageEditPartialImageEvent as ImageEditPartialImageEvent from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam diff --git a/src/openai/types/image_edit_completed_event.py b/src/openai/types/image_edit_completed_event.py new file mode 100644 index 0000000000..a40682da6a --- /dev/null +++ b/src/openai/types/image_edit_completed_event.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageEditCompletedEvent", "Usage", "UsageInputTokensDetails"] + + +class UsageInputTokensDetails(BaseModel): + image_tokens: int + """The number of image tokens in the input prompt.""" + + text_tokens: int + """The number of text tokens in the input prompt.""" + + +class Usage(BaseModel): + input_tokens: int + """The number of tokens (images and text) in the input prompt.""" + + input_tokens_details: UsageInputTokensDetails + """The input tokens detailed information for the image generation.""" + + output_tokens: int + """The number of image tokens in the output image.""" + + total_tokens: int + """The total number of tokens (images and text) used for the image generation.""" + + +class ImageEditCompletedEvent(BaseModel): + b64_json: str + """Base64-encoded final edited image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the edited image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the edited image.""" + + quality: Literal["low", "medium", "high", "auto"] + """The quality setting for the edited image.""" + + size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"] + """The size of the edited image.""" + + type: Literal["image_edit.completed"] + """The type of the event. Always `image_edit.completed`.""" + + usage: Usage + """For `gpt-image-1` only, the token usage information for the image generation.""" diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index aecb98fa6f..d839e2fcbe 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -8,10 +8,10 @@ from .._types import FileTypes from .image_model import ImageModel -__all__ = ["ImageEditParams"] +__all__ = ["ImageEditParamsBase", "ImageEditParamsNonStreaming", "ImageEditParamsStreaming"] -class ImageEditParams(TypedDict, total=False): +class ImageEditParamsBase(TypedDict, total=False): image: Required[Union[FileTypes, List[FileTypes]]] """The image(s) to edit. Must be a supported image file or an array of images. @@ -40,6 +40,13 @@ class ImageEditParams(TypedDict, total=False): be set to either `png` (default value) or `webp`. """ + input_fidelity: Optional[Literal["high", "low"]] + """ + Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + """ + mask: FileTypes """An additional image whose fully transparent areas (e.g. @@ -72,6 +79,14 @@ class ImageEditParams(TypedDict, total=False): `jpeg`, or `webp`. The default value is `png`. """ + partial_images: Optional[int] + """The number of partial images to generate. + + This parameter is used for streaming responses that return partial images. Value + must be between 0 and 3. When set to 0, the response will be a single image sent + in one streaming event. + """ + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] """The quality of the image that will be generated. @@ -101,3 +116,26 @@ class ImageEditParams(TypedDict, total=False): and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ + + +class ImageEditParamsNonStreaming(ImageEditParamsBase, total=False): + stream: Optional[Literal[False]] + """Edit the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + """ + + +class ImageEditParamsStreaming(ImageEditParamsBase): + stream: Required[Literal[True]] + """Edit the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. + """ + + +ImageEditParams = Union[ImageEditParamsNonStreaming, ImageEditParamsStreaming] diff --git a/src/openai/types/image_edit_partial_image_event.py b/src/openai/types/image_edit_partial_image_event.py new file mode 100644 index 0000000000..20da45efc3 --- /dev/null +++ b/src/openai/types/image_edit_partial_image_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageEditPartialImageEvent"] + + +class ImageEditPartialImageEvent(BaseModel): + b64_json: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the requested edited image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the requested edited image.""" + + partial_image_index: int + """0-based index for the partial image (streaming).""" + + quality: Literal["low", "medium", "high", "auto"] + """The quality setting for the requested edited image.""" + + size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"] + """The size of the requested edited image.""" + + type: Literal["image_edit.partial_image"] + """The type of the event. Always `image_edit.partial_image`.""" diff --git a/src/openai/types/image_edit_stream_event.py b/src/openai/types/image_edit_stream_event.py new file mode 100644 index 0000000000..759f6c6db5 --- /dev/null +++ b/src/openai/types/image_edit_stream_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from .._utils import PropertyInfo +from .image_edit_completed_event import ImageEditCompletedEvent +from .image_edit_partial_image_event import ImageEditPartialImageEvent + +__all__ = ["ImageEditStreamEvent"] + +ImageEditStreamEvent: TypeAlias = Annotated[ + Union[ImageEditPartialImageEvent, ImageEditCompletedEvent], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/image_gen_completed_event.py b/src/openai/types/image_gen_completed_event.py new file mode 100644 index 0000000000..e78da842d4 --- /dev/null +++ b/src/openai/types/image_gen_completed_event.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageGenCompletedEvent", "Usage", "UsageInputTokensDetails"] + + +class UsageInputTokensDetails(BaseModel): + image_tokens: int + """The number of image tokens in the input prompt.""" + + text_tokens: int + """The number of text tokens in the input prompt.""" + + +class Usage(BaseModel): + input_tokens: int + """The number of tokens (images and text) in the input prompt.""" + + input_tokens_details: UsageInputTokensDetails + """The input tokens detailed information for the image generation.""" + + output_tokens: int + """The number of image tokens in the output image.""" + + total_tokens: int + """The total number of tokens (images and text) used for the image generation.""" + + +class ImageGenCompletedEvent(BaseModel): + b64_json: str + """Base64-encoded image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the generated image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the generated image.""" + + quality: Literal["low", "medium", "high", "auto"] + """The quality setting for the generated image.""" + + size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"] + """The size of the generated image.""" + + type: Literal["image_generation.completed"] + """The type of the event. Always `image_generation.completed`.""" + + usage: Usage + """For `gpt-image-1` only, the token usage information for the image generation.""" diff --git a/src/openai/types/image_gen_partial_image_event.py b/src/openai/types/image_gen_partial_image_event.py new file mode 100644 index 0000000000..965d450604 --- /dev/null +++ b/src/openai/types/image_gen_partial_image_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["ImageGenPartialImageEvent"] + + +class ImageGenPartialImageEvent(BaseModel): + b64_json: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + background: Literal["transparent", "opaque", "auto"] + """The background setting for the requested image.""" + + created_at: int + """The Unix timestamp when the event was created.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format for the requested image.""" + + partial_image_index: int + """0-based index for the partial image (streaming).""" + + quality: Literal["low", "medium", "high", "auto"] + """The quality setting for the requested image.""" + + size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"] + """The size of the requested image.""" + + type: Literal["image_generation.partial_image"] + """The type of the event. Always `image_generation.partial_image`.""" diff --git a/src/openai/types/image_gen_stream_event.py b/src/openai/types/image_gen_stream_event.py new file mode 100644 index 0000000000..7dde5d5245 --- /dev/null +++ b/src/openai/types/image_gen_stream_event.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from .._utils import PropertyInfo +from .image_gen_completed_event import ImageGenCompletedEvent +from .image_gen_partial_image_event import ImageGenPartialImageEvent + +__all__ = ["ImageGenStreamEvent"] + +ImageGenStreamEvent: TypeAlias = Annotated[ + Union[ImageGenPartialImageEvent, ImageGenCompletedEvent], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 8fc10220dc..bd9f34b28e 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -7,10 +7,10 @@ from .image_model import ImageModel -__all__ = ["ImageGenerateParams"] +__all__ = ["ImageGenerateParamsBase", "ImageGenerateParamsNonStreaming", "ImageGenerateParamsStreaming"] -class ImageGenerateParams(TypedDict, total=False): +class ImageGenerateParamsBase(TypedDict, total=False): prompt: Required[str] """A text description of the desired image(s). @@ -62,6 +62,14 @@ class ImageGenerateParams(TypedDict, total=False): `jpeg`, or `webp`. """ + partial_images: Optional[int] + """The number of partial images to generate. + + This parameter is used for streaming responses that return partial images. Value + must be between 0 and 3. When set to 0, the response will be a single image sent + in one streaming event. + """ + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] """The quality of the image that will be generated. @@ -107,3 +115,26 @@ class ImageGenerateParams(TypedDict, total=False): and detect abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). """ + + +class ImageGenerateParamsNonStreaming(ImageGenerateParamsBase, total=False): + stream: Optional[Literal[False]] + """Generate the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. + """ + + +class ImageGenerateParamsStreaming(ImageGenerateParamsBase): + stream: Required[Literal[True]] + """Generate the image in streaming mode. + + Defaults to `false`. See the + [Image generation guide](https://platform.openai.com/docs/guides/image-generation) + for more information. This parameter is only supported for `gpt-image-1`. + """ + + +ImageGenerateParams = Union[ImageGenerateParamsNonStreaming, ImageGenerateParamsStreaming] diff --git a/src/openai/types/responses/response_output_refusal.py b/src/openai/types/responses/response_output_refusal.py index eba581070d..685c8722a6 100644 --- a/src/openai/types/responses/response_output_refusal.py +++ b/src/openai/types/responses/response_output_refusal.py @@ -9,7 +9,7 @@ class ResponseOutputRefusal(BaseModel): refusal: str - """The refusal explanationfrom the model.""" + """The refusal explanation from the model.""" type: Literal["refusal"] """The type of the refusal. Always `refusal`.""" diff --git a/src/openai/types/responses/response_output_refusal_param.py b/src/openai/types/responses/response_output_refusal_param.py index 53140a6080..54cfaf0791 100644 --- a/src/openai/types/responses/response_output_refusal_param.py +++ b/src/openai/types/responses/response_output_refusal_param.py @@ -9,7 +9,7 @@ class ResponseOutputRefusalParam(TypedDict, total=False): refusal: Required[str] - """The refusal explanationfrom the model.""" + """The refusal explanation from the model.""" type: Required[Literal["refusal"]] """The type of the refusal. Always `refusal`.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 9c1573bda9..4399871e29 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -124,6 +124,13 @@ class ImageGeneration(BaseModel): One of `transparent`, `opaque`, or `auto`. Default: `auto`. """ + input_fidelity: Optional[Literal["high", "low"]] = None + """ + Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + """ + input_image_mask: Optional[ImageGenerationInputImageMask] = None """Optional mask for inpainting. diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 493a1dad9c..a977f06e3f 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -125,6 +125,13 @@ class ImageGeneration(TypedDict, total=False): One of `transparent`, `opaque`, or `auto`. Default: `auto`. """ + input_fidelity: Optional[Literal["high", "low"]] + """ + Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + """ + input_image_mask: ImageGenerationInputImageMask """Optional mask for inpainting. diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 10fc56d685..99fe77d8e0 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -61,7 +61,7 @@ def test_streaming_response_create_variation(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_method_edit(self, client: OpenAI) -> None: + def test_method_edit_overload_1(self, client: OpenAI) -> None: image = client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", @@ -69,25 +69,28 @@ def test_method_edit(self, client: OpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - def test_method_edit_with_all_params(self, client: OpenAI) -> None: + def test_method_edit_with_all_params_overload_1(self, client: OpenAI) -> None: image = client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", background="transparent", + input_fidelity="high", mask=b"raw file contents", model="string", n=1, output_compression=100, output_format="png", + partial_images=1, quality="high", response_format="url", size="1024x1024", + stream=False, user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - def test_raw_response_edit(self, client: OpenAI) -> None: + def test_raw_response_edit_overload_1(self, client: OpenAI) -> None: response = client.images.with_raw_response.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", @@ -99,7 +102,7 @@ def test_raw_response_edit(self, client: OpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - def test_streaming_response_edit(self, client: OpenAI) -> None: + def test_streaming_response_edit_overload_1(self, client: OpenAI) -> None: with client.images.with_streaming_response.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", @@ -113,14 +116,71 @@ def test_streaming_response_edit(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_method_generate(self, client: OpenAI) -> None: + def test_method_edit_overload_2(self, client: OpenAI) -> None: + image_stream = client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + ) + image_stream.response.close() + + @parametrize + def test_method_edit_with_all_params_overload_2(self, client: OpenAI) -> None: + image_stream = client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + background="transparent", + input_fidelity="high", + mask=b"raw file contents", + model="string", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="high", + response_format="url", + size="1024x1024", + user="user-1234", + ) + image_stream.response.close() + + @parametrize + def test_raw_response_edit_overload_2(self, client: OpenAI) -> None: + response = client.images.with_raw_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_edit_overload_2(self, client: OpenAI) -> None: + with client.images.with_streaming_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_generate_overload_1(self, client: OpenAI) -> None: image = client.images.generate( prompt="A cute baby sea otter", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - def test_method_generate_with_all_params(self, client: OpenAI) -> None: + def test_method_generate_with_all_params_overload_1(self, client: OpenAI) -> None: image = client.images.generate( prompt="A cute baby sea otter", background="transparent", @@ -129,16 +189,18 @@ def test_method_generate_with_all_params(self, client: OpenAI) -> None: n=1, output_compression=100, output_format="png", + partial_images=1, quality="medium", response_format="url", size="1024x1024", + stream=False, style="vivid", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - def test_raw_response_generate(self, client: OpenAI) -> None: + def test_raw_response_generate_overload_1(self, client: OpenAI) -> None: response = client.images.with_raw_response.generate( prompt="A cute baby sea otter", ) @@ -149,7 +211,7 @@ def test_raw_response_generate(self, client: OpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - def test_streaming_response_generate(self, client: OpenAI) -> None: + def test_streaming_response_generate_overload_1(self, client: OpenAI) -> None: with client.images.with_streaming_response.generate( prompt="A cute baby sea otter", ) as response: @@ -161,6 +223,59 @@ def test_streaming_response_generate(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_generate_overload_2(self, client: OpenAI) -> None: + image_stream = client.images.generate( + prompt="A cute baby sea otter", + stream=True, + ) + image_stream.response.close() + + @parametrize + def test_method_generate_with_all_params_overload_2(self, client: OpenAI) -> None: + image_stream = client.images.generate( + prompt="A cute baby sea otter", + stream=True, + background="transparent", + model="string", + moderation="low", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="medium", + response_format="url", + size="1024x1024", + style="vivid", + user="user-1234", + ) + image_stream.response.close() + + @parametrize + def test_raw_response_generate_overload_2(self, client: OpenAI) -> None: + response = client.images.with_raw_response.generate( + prompt="A cute baby sea otter", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_generate_overload_2(self, client: OpenAI) -> None: + with client.images.with_streaming_response.generate( + prompt="A cute baby sea otter", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + class TestAsyncImages: parametrize = pytest.mark.parametrize( @@ -211,7 +326,7 @@ async def test_streaming_response_create_variation(self, async_client: AsyncOpen assert cast(Any, response.is_closed) is True @parametrize - async def test_method_edit(self, async_client: AsyncOpenAI) -> None: + async def test_method_edit_overload_1(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", @@ -219,25 +334,28 @@ async def test_method_edit(self, async_client: AsyncOpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_method_edit_with_all_params(self, async_client: AsyncOpenAI) -> None: + async def test_method_edit_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", background="transparent", + input_fidelity="high", mask=b"raw file contents", model="string", n=1, output_compression=100, output_format="png", + partial_images=1, quality="high", response_format="url", size="1024x1024", + stream=False, user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_raw_response_edit(self, async_client: AsyncOpenAI) -> None: + async def test_raw_response_edit_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.images.with_raw_response.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", @@ -249,7 +367,7 @@ async def test_raw_response_edit(self, async_client: AsyncOpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_streaming_response_edit(self, async_client: AsyncOpenAI) -> None: + async def test_streaming_response_edit_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.images.with_streaming_response.edit( image=b"raw file contents", prompt="A cute baby sea otter wearing a beret", @@ -263,14 +381,71 @@ async def test_streaming_response_edit(self, async_client: AsyncOpenAI) -> None: assert cast(Any, response.is_closed) is True @parametrize - async def test_method_generate(self, async_client: AsyncOpenAI) -> None: + async def test_method_edit_overload_2(self, async_client: AsyncOpenAI) -> None: + image_stream = await async_client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + ) + await image_stream.response.aclose() + + @parametrize + async def test_method_edit_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + image_stream = await async_client.images.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + background="transparent", + input_fidelity="high", + mask=b"raw file contents", + model="string", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="high", + response_format="url", + size="1024x1024", + user="user-1234", + ) + await image_stream.response.aclose() + + @parametrize + async def test_raw_response_edit_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.images.with_raw_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_edit_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.images.with_streaming_response.edit( + image=b"raw file contents", + prompt="A cute baby sea otter wearing a beret", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_generate_overload_1(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.generate( prompt="A cute baby sea otter", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_method_generate_with_all_params(self, async_client: AsyncOpenAI) -> None: + async def test_method_generate_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.generate( prompt="A cute baby sea otter", background="transparent", @@ -279,16 +454,18 @@ async def test_method_generate_with_all_params(self, async_client: AsyncOpenAI) n=1, output_compression=100, output_format="png", + partial_images=1, quality="medium", response_format="url", size="1024x1024", + stream=False, style="vivid", user="user-1234", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_raw_response_generate(self, async_client: AsyncOpenAI) -> None: + async def test_raw_response_generate_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.images.with_raw_response.generate( prompt="A cute baby sea otter", ) @@ -299,7 +476,7 @@ async def test_raw_response_generate(self, async_client: AsyncOpenAI) -> None: assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize - async def test_streaming_response_generate(self, async_client: AsyncOpenAI) -> None: + async def test_streaming_response_generate_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.images.with_streaming_response.generate( prompt="A cute baby sea otter", ) as response: @@ -310,3 +487,56 @@ async def test_streaming_response_generate(self, async_client: AsyncOpenAI) -> N assert_matches_type(ImagesResponse, image, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_generate_overload_2(self, async_client: AsyncOpenAI) -> None: + image_stream = await async_client.images.generate( + prompt="A cute baby sea otter", + stream=True, + ) + await image_stream.response.aclose() + + @parametrize + async def test_method_generate_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + image_stream = await async_client.images.generate( + prompt="A cute baby sea otter", + stream=True, + background="transparent", + model="string", + moderation="low", + n=1, + output_compression=100, + output_format="png", + partial_images=1, + quality="medium", + response_format="url", + size="1024x1024", + style="vivid", + user="user-1234", + ) + await image_stream.response.aclose() + + @parametrize + async def test_raw_response_generate_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.images.with_raw_response.generate( + prompt="A cute baby sea otter", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_generate_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.images.with_streaming_response.generate( + prompt="A cute baby sea otter", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True From 35df552d032873b62c2ae127a0efce60947dbed0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 16:25:26 +0000 Subject: [PATCH 019/408] release: 1.97.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6b38a1bd5a..7b33636f46 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.96.1" + ".": "1.97.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 93bfb63f37..2e603f06be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.97.0 (2025-07-16) + +Full Changelog: [v1.96.1...v1.97.0](https://github.com/openai/openai-python/compare/v1.96.1...v1.97.0) + +### Features + +* **api:** manual updates ([ed8e899](https://github.com/openai/openai-python/commit/ed8e89953d11bd5f44fa531422bdbb7a577ab426)) + ## 1.96.1 (2025-07-15) Full Changelog: [v1.96.0...v1.96.1](https://github.com/openai/openai-python/compare/v1.96.0...v1.96.1) diff --git a/pyproject.toml b/pyproject.toml index 0f655d058d..533379d52a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.96.1" +version = "1.97.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 39be0338f6..8e5ed5fa86 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.96.1" # x-release-please-version +__version__ = "1.97.0" # x-release-please-version From fa466c099aab0213f3ce09d5adcfca5ae2bf58a4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:06:17 +0000 Subject: [PATCH 020/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 2b9160cf6e..bc75e5c98c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-670ea0d2cc44f52a87dd3cadea45632953283e0636ba30788fdbdb22a232ccac.yml openapi_spec_hash: d8b7d38911fead545adf3e4297956410 -config_hash: 5525bda35e48ea6387c6175c4d1651fa +config_hash: b2a4028fdbb27a08de89831ed310e244 From c6b933520213cddea927c4fe83c1abe2f66893d8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:27:19 +0000 Subject: [PATCH 021/408] fix(parsing): ignore empty metadata --- src/openai/_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index f347a81dac..dee5551948 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -464,7 +464,7 @@ def construct_type(*, value: object, type_: object, metadata: Optional[List[Any] type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` - if metadata is not None: + if metadata is not None and len(metadata) > 0: meta: tuple[Any, ...] = tuple(metadata) elif is_annotated_type(type_): meta = get_args(type_)[1:] From bf4a9a422e5eaffa90863439ddfd8a82cbaaa636 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 21:17:00 +0000 Subject: [PATCH 022/408] chore(api): event shapes more accurate --- .stats.yml | 6 ++-- api.md | 2 -- src/openai/lib/streaming/responses/_events.py | 4 --- .../lib/streaming/responses/_responses.py | 2 ++ src/openai/resources/audio/speech.py | 8 ++--- .../resources/beta/realtime/sessions.py | 14 +++----- .../resources/chat/completions/completions.py | 12 +++---- src/openai/resources/images.py | 36 +++++++++++++++++++ src/openai/resources/responses/responses.py | 12 +++---- .../types/audio/speech_create_params.py | 6 +--- .../types/beta/realtime/realtime_response.py | 9 ++--- .../beta/realtime/response_create_event.py | 8 ++--- .../realtime/response_create_event_param.py | 6 ++-- src/openai/types/beta/realtime/session.py | 8 ++--- .../beta/realtime/session_create_params.py | 6 ++-- .../beta/realtime/session_create_response.py | 8 ++--- .../beta/realtime/session_update_event.py | 8 ++--- .../realtime/session_update_event_param.py | 6 ++-- src/openai/types/chat/chat_completion.py | 2 +- .../types/chat/chat_completion_audio_param.py | 6 +--- .../types/chat/chat_completion_chunk.py | 2 +- .../types/chat/completion_create_params.py | 2 +- src/openai/types/image_edit_params.py | 3 ++ src/openai/types/image_generate_params.py | 3 ++ src/openai/types/images_response.py | 2 +- src/openai/types/responses/__init__.py | 2 -- src/openai/types/responses/response.py | 2 +- .../response_code_interpreter_tool_call.py | 6 +++- ...sponse_code_interpreter_tool_call_param.py | 6 +++- .../types/responses/response_create_params.py | 2 +- ...response_mcp_call_arguments_delta_event.py | 7 ++-- .../response_mcp_call_arguments_done_event.py | 4 +-- .../response_mcp_call_completed_event.py | 6 ++++ .../response_mcp_call_failed_event.py | 6 ++++ ...response_mcp_list_tools_completed_event.py | 6 ++++ .../response_mcp_list_tools_failed_event.py | 6 ++++ ...sponse_mcp_list_tools_in_progress_event.py | 6 ++++ .../response_reasoning_delta_event.py | 27 -------------- .../response_reasoning_done_event.py | 27 -------------- .../types/responses/response_stream_event.py | 4 --- .../responses/response_text_delta_event.py | 25 ++++++++++++- .../responses/response_text_done_event.py | 25 ++++++++++++- .../types/shared/function_definition.py | 2 +- .../shared_params/function_definition.py | 2 +- 44 files changed, 186 insertions(+), 166 deletions(-) delete mode 100644 src/openai/types/responses/response_reasoning_delta_event.py delete mode 100644 src/openai/types/responses/response_reasoning_done_event.py diff --git a/.stats.yml b/.stats.yml index bc75e5c98c..2dc4f680a9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-670ea0d2cc44f52a87dd3cadea45632953283e0636ba30788fdbdb22a232ccac.yml -openapi_spec_hash: d8b7d38911fead545adf3e4297956410 -config_hash: b2a4028fdbb27a08de89831ed310e244 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b2a451656ca64d30d174391ebfd94806b4de3ab76dc55b92843cfb7f1a54ecb6.yml +openapi_spec_hash: 27d9691b400f28c17ef063a1374048b0 +config_hash: e822d0c9082c8b312264403949243179 diff --git a/api.md b/api.md index b3a2245cdd..0280b886d1 100644 --- a/api.md +++ b/api.md @@ -791,8 +791,6 @@ from openai.types.responses import ( ResponseOutputTextAnnotationAddedEvent, ResponsePrompt, ResponseQueuedEvent, - ResponseReasoningDeltaEvent, - ResponseReasoningDoneEvent, ResponseReasoningItem, ResponseReasoningSummaryDeltaEvent, ResponseReasoningSummaryDoneEvent, diff --git a/src/openai/lib/streaming/responses/_events.py b/src/openai/lib/streaming/responses/_events.py index 6e547815e2..4c8a588944 100644 --- a/src/openai/lib/streaming/responses/_events.py +++ b/src/openai/lib/streaming/responses/_events.py @@ -21,9 +21,7 @@ ResponseRefusalDoneEvent, ResponseRefusalDeltaEvent, ResponseMcpCallFailedEvent, - ResponseReasoningDoneEvent, ResponseOutputItemDoneEvent, - ResponseReasoningDeltaEvent, ResponseContentPartDoneEvent, ResponseOutputItemAddedEvent, ResponseContentPartAddedEvent, @@ -139,10 +137,8 @@ class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[Te ResponseMcpListToolsInProgressEvent, ResponseOutputTextAnnotationAddedEvent, ResponseQueuedEvent, - ResponseReasoningDeltaEvent, ResponseReasoningSummaryDeltaEvent, ResponseReasoningSummaryDoneEvent, - ResponseReasoningDoneEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/lib/streaming/responses/_responses.py b/src/openai/lib/streaming/responses/_responses.py index 2c2fec5469..d45664de45 100644 --- a/src/openai/lib/streaming/responses/_responses.py +++ b/src/openai/lib/streaming/responses/_responses.py @@ -264,6 +264,7 @@ def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEven item_id=event.item_id, output_index=event.output_index, sequence_number=event.sequence_number, + logprobs=event.logprobs, type="response.output_text.delta", snapshot=content.text, ) @@ -282,6 +283,7 @@ def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEven item_id=event.item_id, output_index=event.output_index, sequence_number=event.sequence_number, + logprobs=event.logprobs, type="response.output_text.done", text=event.text, parsed=parse_text(event.text, text_format=self._text_format), diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index fe776baae8..6251cfed4e 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -50,9 +50,7 @@ def create( *, input: str, model: Union[str, SpeechModel], - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ], + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]], instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, @@ -146,9 +144,7 @@ async def create( *, input: str, model: Union[str, SpeechModel], - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ], + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]], instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index 77f1ec9059..e639c0ba43 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -66,9 +66,7 @@ def create( tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, tracing: session_create_params.Tracing | NotGiven = NOT_GIVEN, turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ] + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -163,8 +161,7 @@ def create( voice: The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are - `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, - `shimmer`, and `verse`. + `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. extra_headers: Send extra headers @@ -251,9 +248,7 @@ async def create( tools: Iterable[session_create_params.Tool] | NotGiven = NOT_GIVEN, tracing: session_create_params.Tracing | NotGiven = NOT_GIVEN, turn_detection: session_create_params.TurnDetection | NotGiven = NOT_GIVEN, - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ] + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -348,8 +343,7 @@ async def create( voice: The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are - `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, - `shimmer`, and `verse`. + `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. extra_headers: Send extra headers diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 5806296773..739aa662d4 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -417,7 +417,7 @@ def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -697,7 +697,7 @@ def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -968,7 +968,7 @@ def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -1784,7 +1784,7 @@ async def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -2064,7 +2064,7 @@ async def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -2335,7 +2335,7 @@ async def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 77b7a1b24e..c8eda8a76f 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -196,6 +196,9 @@ def edit( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -310,6 +313,9 @@ def edit( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -420,6 +426,9 @@ def edit( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -579,6 +588,9 @@ def generate( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the @@ -690,6 +702,9 @@ def generate( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the @@ -797,6 +812,9 @@ def generate( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the @@ -1066,6 +1084,9 @@ async def edit( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -1180,6 +1201,9 @@ async def edit( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -1290,6 +1314,9 @@ async def edit( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`. @@ -1449,6 +1476,9 @@ async def generate( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the @@ -1560,6 +1590,9 @@ async def generate( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the @@ -1667,6 +1700,9 @@ async def generate( responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. + quality: The quality of the image that will be generated. - `auto` (default value) will automatically select the best quality for the diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index ce132bdb05..fe99aa851d 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -198,7 +198,7 @@ def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -414,7 +414,7 @@ def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -623,7 +623,7 @@ def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -1463,7 +1463,7 @@ async def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -1679,7 +1679,7 @@ async def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service @@ -1888,7 +1888,7 @@ async def create( - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 4ee4a3c4e4..feeb68c68b 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -20,11 +20,7 @@ class SpeechCreateParams(TypedDict, total=False): `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. """ - voice: Required[ - Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ] - ] + voice: Required[Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]]] """The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, diff --git a/src/openai/types/beta/realtime/realtime_response.py b/src/openai/types/beta/realtime/realtime_response.py index 28e03c8717..ccc97c5d22 100644 --- a/src/openai/types/beta/realtime/realtime_response.py +++ b/src/openai/types/beta/realtime/realtime_response.py @@ -80,13 +80,8 @@ class RealtimeResponse(BaseModel): will become the input for later turns. """ - voice: Union[ - str, - Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], - None, - ] = None + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None """ The voice the model used to respond. Current voice options are `alloy`, `ash`, - `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and - `verse`. + `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/response_create_event.py b/src/openai/types/beta/realtime/response_create_event.py index 3b8a6de8df..7219cedbf3 100644 --- a/src/openai/types/beta/realtime/response_create_event.py +++ b/src/openai/types/beta/realtime/response_create_event.py @@ -101,16 +101,12 @@ class Response(BaseModel): tools: Optional[List[ResponseTool]] = None """Tools (functions) available to the model.""" - voice: Union[ - str, - Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], - None, - ] = None + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. + `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/response_create_event_param.py b/src/openai/types/beta/realtime/response_create_event_param.py index c569d507a0..b4d54bba92 100644 --- a/src/openai/types/beta/realtime/response_create_event_param.py +++ b/src/openai/types/beta/realtime/response_create_event_param.py @@ -102,14 +102,12 @@ class Response(TypedDict, total=False): tools: Iterable[ResponseTool] """Tools (functions) available to the model.""" - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ] + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. + `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py index 606fd83851..f84b3ee4a0 100644 --- a/src/openai/types/beta/realtime/session.py +++ b/src/openai/types/beta/realtime/session.py @@ -268,14 +268,10 @@ class Session(BaseModel): natural conversations, but may have a higher latency. """ - voice: Union[ - str, - Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], - None, - ] = None + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. + `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index e04985d2b6..6be09d8bae 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -145,14 +145,12 @@ class SessionCreateParams(TypedDict, total=False): natural conversations, but may have a higher latency. """ - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ] + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. + `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session_create_response.py b/src/openai/types/beta/realtime/session_create_response.py index 15d5c1742b..471da03691 100644 --- a/src/openai/types/beta/realtime/session_create_response.py +++ b/src/openai/types/beta/realtime/session_create_response.py @@ -187,14 +187,10 @@ class SessionCreateResponse(BaseModel): speech. """ - voice: Union[ - str, - Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], - None, - ] = None + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo` `sage`, `shimmer` and `verse`. + `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index 789b9cd1e5..5b4185dbf6 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -290,16 +290,12 @@ class Session(BaseModel): natural conversations, but may have a higher latency. """ - voice: Union[ - str, - Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"], - None, - ] = None + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"], None] = None """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. + `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index 2dfa2c26f3..3063449bfd 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -288,14 +288,12 @@ class Session(TypedDict, total=False): natural conversations, but may have a higher latency. """ - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ] + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]] """The voice the model uses to respond. Voice cannot be changed during the session once the model has responded with audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and `verse`. + `coral`, `echo`, `sage`, `shimmer`, and `verse`. """ diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index afc23e3f3d..42463f7ec8 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -65,7 +65,7 @@ class ChatCompletion(BaseModel): - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index 25caada177..dc68159c1e 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -15,11 +15,7 @@ class ChatCompletionAudioParam(TypedDict, total=False): Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. """ - voice: Required[ - Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"] - ] - ] + voice: Required[Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]]] """The voice the model uses to respond. Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index da6e315830..082bb6cc19 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -134,7 +134,7 @@ class ChatCompletionChunk(BaseModel): - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 44ea853041..191793c18f 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -214,7 +214,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index d839e2fcbe..c0481012e4 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -85,6 +85,9 @@ class ImageEditParamsBase(TypedDict, total=False): This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. """ quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index bd9f34b28e..e9e9292cc2 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -68,6 +68,9 @@ class ImageGenerateParamsBase(TypedDict, total=False): This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to 0, the response will be a single image sent in one streaming event. + + Note that the final image may be sent before the full number of partial images + are generated if the full image is generated more quickly. """ quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py index 2a8ca728ab..89cc71df24 100644 --- a/src/openai/types/images_response.py +++ b/src/openai/types/images_response.py @@ -25,7 +25,7 @@ class Usage(BaseModel): """The input tokens detailed information for the image generation.""" output_tokens: int - """The number of image tokens in the output image.""" + """The number of output tokens generated by the model.""" total_tokens: int """The total number of tokens (images and text) used for the image generation.""" diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 4316e47730..b563035e78 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -81,11 +81,9 @@ from .response_refusal_delta_event import ResponseRefusalDeltaEvent as ResponseRefusalDeltaEvent from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam -from .response_reasoning_done_event import ResponseReasoningDoneEvent as ResponseReasoningDoneEvent from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent -from .response_reasoning_delta_event import ResponseReasoningDeltaEvent as ResponseReasoningDeltaEvent from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent from .response_function_tool_call_item import ResponseFunctionToolCallItem as ResponseFunctionToolCallItem diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index db85d87f4e..2af85d03fb 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -176,7 +176,7 @@ class Response(BaseModel): - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service diff --git a/src/openai/types/responses/response_code_interpreter_tool_call.py b/src/openai/types/responses/response_code_interpreter_tool_call.py index 7e4dc9f984..257937118b 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call.py @@ -45,7 +45,11 @@ class ResponseCodeInterpreterToolCall(BaseModel): """ status: Literal["in_progress", "completed", "incomplete", "interpreting", "failed"] - """The status of the code interpreter tool call.""" + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ type: Literal["code_interpreter_call"] """The type of the code interpreter tool call. Always `code_interpreter_call`.""" diff --git a/src/openai/types/responses/response_code_interpreter_tool_call_param.py b/src/openai/types/responses/response_code_interpreter_tool_call_param.py index 69e01f99ed..435091001f 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call_param.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call_param.py @@ -44,7 +44,11 @@ class ResponseCodeInterpreterToolCallParam(TypedDict, total=False): """ status: Required[Literal["in_progress", "completed", "incomplete", "interpreting", "failed"]] - """The status of the code interpreter tool call.""" + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ type: Required[Literal["code_interpreter_call"]] """The type of the code interpreter tool call. Always `code_interpreter_call`.""" diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 0187e1fda8..08feefd081 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -136,7 +136,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): - If set to 'auto', then the request will be processed with the service tier configured in the Project settings. Unless otherwise configured, the Project will use 'default'. - - If set to 'default', then the requset will be processed with the standard + - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or 'priority', then the request will be processed with the corresponding service diff --git a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py index 8481506dc3..54eff38373 100644 --- a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py +++ b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py @@ -8,8 +8,11 @@ class ResponseMcpCallArgumentsDeltaEvent(BaseModel): - delta: object - """The partial update to the arguments for the MCP tool call.""" + delta: str + """ + A JSON string containing the partial update to the arguments for the MCP tool + call. + """ item_id: str """The unique identifier of the MCP tool call item being processed.""" diff --git a/src/openai/types/responses/response_mcp_call_arguments_done_event.py b/src/openai/types/responses/response_mcp_call_arguments_done_event.py index 4be09d4862..59ce9bc944 100644 --- a/src/openai/types/responses/response_mcp_call_arguments_done_event.py +++ b/src/openai/types/responses/response_mcp_call_arguments_done_event.py @@ -8,8 +8,8 @@ class ResponseMcpCallArgumentsDoneEvent(BaseModel): - arguments: object - """The finalized arguments for the MCP tool call.""" + arguments: str + """A JSON string containing the finalized arguments for the MCP tool call.""" item_id: str """The unique identifier of the MCP tool call item being processed.""" diff --git a/src/openai/types/responses/response_mcp_call_completed_event.py b/src/openai/types/responses/response_mcp_call_completed_event.py index 009fbc3c60..2fee5dff81 100644 --- a/src/openai/types/responses/response_mcp_call_completed_event.py +++ b/src/openai/types/responses/response_mcp_call_completed_event.py @@ -8,6 +8,12 @@ class ResponseMcpCallCompletedEvent(BaseModel): + item_id: str + """The ID of the MCP tool call item that completed.""" + + output_index: int + """The index of the output item that completed.""" + sequence_number: int """The sequence number of this event.""" diff --git a/src/openai/types/responses/response_mcp_call_failed_event.py b/src/openai/types/responses/response_mcp_call_failed_event.py index e6edc6ded5..ca41ab7159 100644 --- a/src/openai/types/responses/response_mcp_call_failed_event.py +++ b/src/openai/types/responses/response_mcp_call_failed_event.py @@ -8,6 +8,12 @@ class ResponseMcpCallFailedEvent(BaseModel): + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + sequence_number: int """The sequence number of this event.""" diff --git a/src/openai/types/responses/response_mcp_list_tools_completed_event.py b/src/openai/types/responses/response_mcp_list_tools_completed_event.py index 6290c3cf9f..c60ad88ee5 100644 --- a/src/openai/types/responses/response_mcp_list_tools_completed_event.py +++ b/src/openai/types/responses/response_mcp_list_tools_completed_event.py @@ -8,6 +8,12 @@ class ResponseMcpListToolsCompletedEvent(BaseModel): + item_id: str + """The ID of the MCP tool call item that produced this output.""" + + output_index: int + """The index of the output item that was processed.""" + sequence_number: int """The sequence number of this event.""" diff --git a/src/openai/types/responses/response_mcp_list_tools_failed_event.py b/src/openai/types/responses/response_mcp_list_tools_failed_event.py index 1f6e325b36..0c966c447a 100644 --- a/src/openai/types/responses/response_mcp_list_tools_failed_event.py +++ b/src/openai/types/responses/response_mcp_list_tools_failed_event.py @@ -8,6 +8,12 @@ class ResponseMcpListToolsFailedEvent(BaseModel): + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + sequence_number: int """The sequence number of this event.""" diff --git a/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py b/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py index 236e5fe6e7..f451db1ed5 100644 --- a/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py +++ b/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py @@ -8,6 +8,12 @@ class ResponseMcpListToolsInProgressEvent(BaseModel): + item_id: str + """The ID of the MCP tool call item that is being processed.""" + + output_index: int + """The index of the output item that is being processed.""" + sequence_number: int """The sequence number of this event.""" diff --git a/src/openai/types/responses/response_reasoning_delta_event.py b/src/openai/types/responses/response_reasoning_delta_event.py deleted file mode 100644 index f37d3d370c..0000000000 --- a/src/openai/types/responses/response_reasoning_delta_event.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ResponseReasoningDeltaEvent"] - - -class ResponseReasoningDeltaEvent(BaseModel): - content_index: int - """The index of the reasoning content part within the output item.""" - - delta: object - """The partial update to the reasoning content.""" - - item_id: str - """The unique identifier of the item for which reasoning is being updated.""" - - output_index: int - """The index of the output item in the response's output array.""" - - sequence_number: int - """The sequence number of this event.""" - - type: Literal["response.reasoning.delta"] - """The type of the event. Always 'response.reasoning.delta'.""" diff --git a/src/openai/types/responses/response_reasoning_done_event.py b/src/openai/types/responses/response_reasoning_done_event.py deleted file mode 100644 index 9f8b127d7e..0000000000 --- a/src/openai/types/responses/response_reasoning_done_event.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ResponseReasoningDoneEvent"] - - -class ResponseReasoningDoneEvent(BaseModel): - content_index: int - """The index of the reasoning content part within the output item.""" - - item_id: str - """The unique identifier of the item for which reasoning is finalized.""" - - output_index: int - """The index of the output item in the response's output array.""" - - sequence_number: int - """The sequence number of this event.""" - - text: str - """The finalized reasoning text.""" - - type: Literal["response.reasoning.done"] - """The type of the event. Always 'response.reasoning.done'.""" diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py index 24a83f1aa2..98e1d6c34d 100644 --- a/src/openai/types/responses/response_stream_event.py +++ b/src/openai/types/responses/response_stream_event.py @@ -17,9 +17,7 @@ from .response_in_progress_event import ResponseInProgressEvent from .response_refusal_done_event import ResponseRefusalDoneEvent from .response_refusal_delta_event import ResponseRefusalDeltaEvent -from .response_reasoning_done_event import ResponseReasoningDoneEvent from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent -from .response_reasoning_delta_event import ResponseReasoningDeltaEvent from .response_output_item_done_event import ResponseOutputItemDoneEvent from .response_content_part_done_event import ResponseContentPartDoneEvent from .response_output_item_added_event import ResponseOutputItemAddedEvent @@ -111,8 +109,6 @@ ResponseMcpListToolsInProgressEvent, ResponseOutputTextAnnotationAddedEvent, ResponseQueuedEvent, - ResponseReasoningDeltaEvent, - ResponseReasoningDoneEvent, ResponseReasoningSummaryDeltaEvent, ResponseReasoningSummaryDoneEvent, ], diff --git a/src/openai/types/responses/response_text_delta_event.py b/src/openai/types/responses/response_text_delta_event.py index 7e4aec7024..b5379b7ac3 100644 --- a/src/openai/types/responses/response_text_delta_event.py +++ b/src/openai/types/responses/response_text_delta_event.py @@ -1,10 +1,30 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["ResponseTextDeltaEvent"] +__all__ = ["ResponseTextDeltaEvent", "Logprob", "LogprobTopLogprob"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probability of the top 20 most likely tokens.""" class ResponseTextDeltaEvent(BaseModel): @@ -17,6 +37,9 @@ class ResponseTextDeltaEvent(BaseModel): item_id: str """The ID of the output item that the text delta was added to.""" + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + output_index: int """The index of the output item that the text delta was added to.""" diff --git a/src/openai/types/responses/response_text_done_event.py b/src/openai/types/responses/response_text_done_event.py index 0d5ed4dd19..d9776a1844 100644 --- a/src/openai/types/responses/response_text_done_event.py +++ b/src/openai/types/responses/response_text_done_event.py @@ -1,10 +1,30 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["ResponseTextDoneEvent"] +__all__ = ["ResponseTextDoneEvent", "Logprob", "LogprobTopLogprob"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probability of the top 20 most likely tokens.""" class ResponseTextDoneEvent(BaseModel): @@ -14,6 +34,9 @@ class ResponseTextDoneEvent(BaseModel): item_id: str """The ID of the output item that the text content is finalized.""" + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + output_index: int """The index of the output item that the text content is finalized.""" diff --git a/src/openai/types/shared/function_definition.py b/src/openai/types/shared/function_definition.py index 06baa23170..33ebb9ad3e 100644 --- a/src/openai/types/shared/function_definition.py +++ b/src/openai/types/shared/function_definition.py @@ -39,5 +39,5 @@ class FunctionDefinition(BaseModel): If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the - [function calling guide](docs/guides/function-calling). + [function calling guide](https://platform.openai.com/docs/guides/function-calling). """ diff --git a/src/openai/types/shared_params/function_definition.py b/src/openai/types/shared_params/function_definition.py index d45ec13f1e..b3fdaf86ff 100644 --- a/src/openai/types/shared_params/function_definition.py +++ b/src/openai/types/shared_params/function_definition.py @@ -41,5 +41,5 @@ class FunctionDefinition(TypedDict, total=False): If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the - [function calling guide](docs/guides/function-calling). + [function calling guide](https://platform.openai.com/docs/guides/function-calling). """ From 48df6b4c30d7e4b1f8a60cf3d34bce8dab06a30b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:04:02 +0000 Subject: [PATCH 023/408] fix(parsing): parse extra field types --- src/openai/_models.py | 25 +++++++++++++++++++++++-- tests/test_models.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index dee5551948..d84d51d913 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -233,14 +233,18 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] else: fields_values[name] = field_get_default(field) + extra_field_type = _get_extra_fields_type(__cls) + _extra = {} for key, value in values.items(): if key not in model_fields: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + if PYDANTIC_V2: - _extra[key] = value + _extra[key] = parsed else: _fields_set.add(key) - fields_values[key] = value + fields_values[key] = parsed object.__setattr__(m, "__dict__", fields_values) @@ -395,6 +399,23 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if not PYDANTIC_V2: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None + + def is_basemodel(type_: type) -> bool: """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" if is_union(type_): diff --git a/tests/test_models.py b/tests/test_models.py index 7262f45006..54a3a32048 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone from typing_extensions import Literal, Annotated, TypeAliasType @@ -934,3 +934,30 @@ class Type2(BaseModel): ) assert isinstance(model, Type1) assert isinstance(model.value, InnerType2) + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="this is only supported in pydantic v2 for now") +def test_extra_properties() -> None: + class Item(BaseModel): + prop: int + + class Model(BaseModel): + __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + other: str + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Item: ... + + model = construct_type( + type_=Model, + value={ + "a": {"prop": 1}, + "other": "foo", + }, + ) + assert isinstance(model, Model) + assert model.a.prop == 1 + assert isinstance(model.a, Item) + assert model.other == "foo" From e6c6757553bbdb777c31d0daf5916fb9e2b47ff8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:04:35 +0000 Subject: [PATCH 024/408] release: 1.97.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7b33636f46..9cdfd7b049 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.97.0" + ".": "1.97.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e603f06be..0c8d06cbb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.97.1 (2025-07-22) + +Full Changelog: [v1.97.0...v1.97.1](https://github.com/openai/openai-python/compare/v1.97.0...v1.97.1) + +### Bug Fixes + +* **parsing:** ignore empty metadata ([58c359f](https://github.com/openai/openai-python/commit/58c359ff67fd6103268e4405600fd58844b6f27b)) +* **parsing:** parse extra field types ([d524b7e](https://github.com/openai/openai-python/commit/d524b7e201418ccc9b5c2206da06d1be011808e5)) + + +### Chores + +* **api:** event shapes more accurate ([f3a9a92](https://github.com/openai/openai-python/commit/f3a9a9229280ecb7e0b2779dd44290df6d9824ef)) + ## 1.97.0 (2025-07-16) Full Changelog: [v1.96.1...v1.97.0](https://github.com/openai/openai-python/compare/v1.96.1...v1.97.0) diff --git a/pyproject.toml b/pyproject.toml index 533379d52a..af1366b34e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.97.0" +version = "1.97.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 8e5ed5fa86..9073c643cc 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.97.0" # x-release-please-version +__version__ = "1.97.1" # x-release-please-version From 48188cc8d5af8c8c4359f84848ea9e436739819f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 07:40:14 -0400 Subject: [PATCH 025/408] release: 1.97.2 (#2494) * codegen metadata * fix(parsing): ignore empty metadata * chore(internal): refactor stream event processing to be more future proof * fixup! * fixup! * fixup! * update comment * chore(project): add settings file for vscode * flip logic around * release: 1.97.2 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: David Meadows --- .gitignore | 1 - .release-please-manifest.json | 2 +- .vscode/settings.json | 3 +++ CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_streaming.py | 33 ++++++++++++++------------------- src/openai/_version.py | 2 +- 7 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 70815df7f6..55c6ca861f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .prism.log -.vscode _dev __pycache__ diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9cdfd7b049..1137af1259 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.97.1" + ".": "1.97.2" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..5b01030785 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.importFormat": "relative", +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c8d06cbb6..945e224cf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.97.2 (2025-07-30) + +Full Changelog: [v1.97.1...v1.97.2](https://github.com/openai/openai-python/compare/v1.97.1...v1.97.2) + +### Chores + +* **client:** refactor streaming slightly to better future proof it ([71c0c74](https://github.com/openai/openai-python/commit/71c0c747132221b798e419bc5a37baf67173d34e)) +* **project:** add settings file for vscode ([29c22c9](https://github.com/openai/openai-python/commit/29c22c90fd229983355089f95d0bba9de15efedb)) + ## 1.97.1 (2025-07-22) Full Changelog: [v1.97.0...v1.97.1](https://github.com/openai/openai-python/compare/v1.97.0...v1.97.1) diff --git a/pyproject.toml b/pyproject.toml index af1366b34e..5b59053d02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.97.1" +version = "1.97.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index fa0a30e183..f586de74ff 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -59,14 +59,11 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None or ( - sse.event.startswith("response.") or - sse.event.startswith("transcript.") or - sse.event.startswith("image_edit.") or - sse.event.startswith("image_generation.") - ): + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + if sse.event and sse.event.startswith("thread."): data = sse.json() - if is_mapping(data) and data.get("error"): + + if sse.event == "error" and is_mapping(data) and data.get("error"): message = None error = data.get("error") if is_mapping(error): @@ -80,12 +77,10 @@ def __stream__(self) -> Iterator[_T]: body=data["error"], ) - yield process_data(data=data, cast_to=cast_to, response=response) - + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) else: data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): + if is_mapping(data) and data.get("error"): message = None error = data.get("error") if is_mapping(error): @@ -99,7 +94,7 @@ def __stream__(self) -> Iterator[_T]: body=data["error"], ) - yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + yield process_data(data=data, cast_to=cast_to, response=response) # Ensure the entire stream is consumed for _sse in iterator: @@ -166,9 +161,11 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.data.startswith("[DONE]"): break - if sse.event is None or sse.event.startswith("response.") or sse.event.startswith("transcript."): + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + if sse.event and sse.event.startswith("thread."): data = sse.json() - if is_mapping(data) and data.get("error"): + + if sse.event == "error" and is_mapping(data) and data.get("error"): message = None error = data.get("error") if is_mapping(error): @@ -182,12 +179,10 @@ async def __stream__(self) -> AsyncIterator[_T]: body=data["error"], ) - yield process_data(data=data, cast_to=cast_to, response=response) - + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) else: data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): + if is_mapping(data) and data.get("error"): message = None error = data.get("error") if is_mapping(error): @@ -201,7 +196,7 @@ async def __stream__(self) -> AsyncIterator[_T]: body=data["error"], ) - yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + yield process_data(data=data, cast_to=cast_to, response=response) # Ensure the entire stream is consumed async for _sse in iterator: diff --git a/src/openai/_version.py b/src/openai/_version.py index 9073c643cc..59fb46ac23 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.97.1" # x-release-please-version +__version__ = "1.97.2" # x-release-please-version From a3315d9fcc17d7583603476f088929fb2b9e71ca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 08:47:13 -0400 Subject: [PATCH 026/408] release: 1.98.0 (#2503) * feat(api): manual updates * release: 1.98.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +- CHANGELOG.md | 8 ++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- .../resources/chat/completions/completions.py | 128 +++++++++++++++--- src/openai/resources/responses/responses.py | 128 +++++++++++++++--- src/openai/types/chat/__init__.py | 2 + .../chat_completion_content_part_image.py | 27 ++++ .../chat/chat_completion_content_part_text.py | 15 ++ .../chat/chat_completion_store_message.py | 15 +- .../types/chat/completion_create_params.py | 25 +++- src/openai/types/responses/response.py | 25 +++- .../types/responses/response_create_params.py | 25 +++- tests/api_resources/chat/test_completions.py | 8 ++ tests/api_resources/test_responses.py | 8 ++ 16 files changed, 371 insertions(+), 55 deletions(-) create mode 100644 src/openai/types/chat/chat_completion_content_part_image.py create mode 100644 src/openai/types/chat/chat_completion_content_part_text.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1137af1259..d12300ea76 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.97.2" + ".": "1.98.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2dc4f680a9..e7fb0bdf9b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b2a451656ca64d30d174391ebfd94806b4de3ab76dc55b92843cfb7f1a54ecb6.yml -openapi_spec_hash: 27d9691b400f28c17ef063a1374048b0 -config_hash: e822d0c9082c8b312264403949243179 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-721e6ccaa72205ee14c71f8163129920464fb814b95d3df9567a9476bbd9b7fb.yml +openapi_spec_hash: 2115413a21df8b5bf9e4552a74df4312 +config_hash: 9606bb315a193bfd8da0459040143242 diff --git a/CHANGELOG.md b/CHANGELOG.md index 945e224cf9..669d5a5792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.98.0 (2025-07-30) + +Full Changelog: [v1.97.2...v1.98.0](https://github.com/openai/openai-python/compare/v1.97.2...v1.98.0) + +### Features + +* **api:** manual updates ([88a8036](https://github.com/openai/openai-python/commit/88a8036c5ea186f36c57029ef4501a0833596f56)) + ## 1.97.2 (2025-07-30) Full Changelog: [v1.97.1...v1.97.2](https://github.com/openai/openai-python/compare/v1.97.1...v1.97.2) diff --git a/pyproject.toml b/pyproject.toml index 5b59053d02..6765611fc2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.97.2" +version = "1.98.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 59fb46ac23..ca890665bc 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.97.2" # x-release-please-version +__version__ = "1.98.0" # x-release-please-version diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 739aa662d4..c851851418 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -248,8 +248,10 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -388,6 +390,10 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning_effort: **o-series models only** Constrains effort on reasoning for @@ -406,6 +412,12 @@ def create( ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. Determinism is not guaranteed, and you @@ -481,9 +493,11 @@ def create( We generally recommend altering this or `temperature` but not both. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -520,8 +534,10 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -668,6 +684,10 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning_effort: **o-series models only** Constrains effort on reasoning for @@ -686,6 +706,12 @@ def create( ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. Determinism is not guaranteed, and you @@ -752,9 +778,11 @@ def create( We generally recommend altering this or `temperature` but not both. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -791,8 +819,10 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -939,6 +969,10 @@ def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning_effort: **o-series models only** Constrains effort on reasoning for @@ -957,6 +991,12 @@ def create( ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. Determinism is not guaranteed, and you @@ -1023,9 +1063,11 @@ def create( We generally recommend altering this or `temperature` but not both. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -1061,8 +1103,10 @@ def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -1104,8 +1148,10 @@ def create( "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": response_format, + "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, "stop": stop, @@ -1615,8 +1661,10 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -1755,6 +1803,10 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning_effort: **o-series models only** Constrains effort on reasoning for @@ -1773,6 +1825,12 @@ async def create( ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. Determinism is not guaranteed, and you @@ -1848,9 +1906,11 @@ async def create( We generally recommend altering this or `temperature` but not both. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -1887,8 +1947,10 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -2035,6 +2097,10 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning_effort: **o-series models only** Constrains effort on reasoning for @@ -2053,6 +2119,12 @@ async def create( ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. Determinism is not guaranteed, and you @@ -2119,9 +2191,11 @@ async def create( We generally recommend altering this or `temperature` but not both. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -2158,8 +2232,10 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -2306,6 +2382,10 @@ async def create( whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning_effort: **o-series models only** Constrains effort on reasoning for @@ -2324,6 +2404,12 @@ async def create( ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + seed: This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. Determinism is not guaranteed, and you @@ -2390,9 +2476,11 @@ async def create( We generally recommend altering this or `temperature` but not both. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -2428,8 +2516,10 @@ async def create( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -2471,8 +2561,10 @@ async def create( "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": response_format, + "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, "stop": stop, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index fe99aa851d..8de46dbab8 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -87,7 +87,9 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -188,11 +190,21 @@ def create( prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning: **o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier @@ -267,9 +279,11 @@ def create( - `disabled` (default): If a model response will exceed the context window size for a model, the request will fail with a 400 error. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). extra_headers: Send extra headers @@ -297,7 +311,9 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -404,11 +420,21 @@ def create( prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning: **o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier @@ -476,9 +502,11 @@ def create( - `disabled` (default): If a model response will exceed the context window size for a model, the request will fail with a 400 error. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). extra_headers: Send extra headers @@ -506,7 +534,9 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -613,11 +643,21 @@ def create( prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning: **o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier @@ -685,9 +725,11 @@ def create( - `disabled` (default): If a model response will exceed the context window size for a model, the request will fail with a 400 error. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). extra_headers: Send extra headers @@ -713,7 +755,9 @@ def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -747,7 +791,9 @@ def create( "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, + "prompt_cache_key": prompt_cache_key, "reasoning": reasoning, + "safety_identifier": safety_identifier, "service_tier": service_tier, "store": store, "stream": stream, @@ -1352,7 +1398,9 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, @@ -1453,11 +1501,21 @@ async def create( prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning: **o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier @@ -1532,9 +1590,11 @@ async def create( - `disabled` (default): If a model response will exceed the context window size for a model, the request will fail with a 400 error. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). extra_headers: Send extra headers @@ -1562,7 +1622,9 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1669,11 +1731,21 @@ async def create( prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning: **o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier @@ -1741,9 +1813,11 @@ async def create( - `disabled` (default): If a model response will exceed the context window size for a model, the request will fail with a 400 error. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). extra_headers: Send extra headers @@ -1771,7 +1845,9 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1878,11 +1954,21 @@ async def create( prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + reasoning: **o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + service_tier: Specifies the processing type used for serving the request. - If set to 'auto', then the request will be processed with the service tier @@ -1950,9 +2036,11 @@ async def create( - `disabled` (default): If a model response will exceed the context window size for a model, the request will fail with a 400 error. - user: A stable identifier for your end-users. Used to boost cache hit rates by better - bucketing similar requests and to help OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). extra_headers: Send extra headers @@ -1978,7 +2066,9 @@ async def create( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -2012,7 +2102,9 @@ async def create( "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, + "prompt_cache_key": prompt_cache_key, "reasoning": reasoning, + "safety_identifier": safety_identifier, "service_tier": service_tier, "store": store, "stream": stream, diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 0945bcad11..dc26198567 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -28,7 +28,9 @@ from .chat_completion_store_message import ChatCompletionStoreMessage as ChatCompletionStoreMessage from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort as ChatCompletionReasoningEffort +from .chat_completion_content_part_text import ChatCompletionContentPartText as ChatCompletionContentPartText from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall +from .chat_completion_content_part_image import ChatCompletionContentPartImage as ChatCompletionContentPartImage from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam from .chat_completion_user_message_param import ChatCompletionUserMessageParam as ChatCompletionUserMessageParam diff --git a/src/openai/types/chat/chat_completion_content_part_image.py b/src/openai/types/chat/chat_completion_content_part_image.py new file mode 100644 index 0000000000..c1386b9dd3 --- /dev/null +++ b/src/openai/types/chat/chat_completion_content_part_image.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionContentPartImage", "ImageURL"] + + +class ImageURL(BaseModel): + url: str + """Either a URL of the image or the base64 encoded image data.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """Specifies the detail level of the image. + + Learn more in the + [Vision guide](https://platform.openai.com/docs/guides/vision#low-or-high-fidelity-image-understanding). + """ + + +class ChatCompletionContentPartImage(BaseModel): + image_url: ImageURL + + type: Literal["image_url"] + """The type of the content part.""" diff --git a/src/openai/types/chat/chat_completion_content_part_text.py b/src/openai/types/chat/chat_completion_content_part_text.py new file mode 100644 index 0000000000..f09f35f708 --- /dev/null +++ b/src/openai/types/chat/chat_completion_content_part_text.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionContentPartText"] + + +class ChatCompletionContentPartText(BaseModel): + text: str + """The text content.""" + + type: Literal["text"] + """The type of the content part.""" diff --git a/src/openai/types/chat/chat_completion_store_message.py b/src/openai/types/chat/chat_completion_store_message.py index 8dc093f7b8..661342716b 100644 --- a/src/openai/types/chat/chat_completion_store_message.py +++ b/src/openai/types/chat/chat_completion_store_message.py @@ -1,10 +1,23 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Union, Optional +from typing_extensions import TypeAlias + from .chat_completion_message import ChatCompletionMessage +from .chat_completion_content_part_text import ChatCompletionContentPartText +from .chat_completion_content_part_image import ChatCompletionContentPartImage + +__all__ = ["ChatCompletionStoreMessage", "ChatCompletionStoreMessageContentPart"] -__all__ = ["ChatCompletionStoreMessage"] +ChatCompletionStoreMessageContentPart: TypeAlias = Union[ChatCompletionContentPartText, ChatCompletionContentPartImage] class ChatCompletionStoreMessage(ChatCompletionMessage): id: str """The identifier of the chat message.""" + + content_parts: Optional[List[ChatCompletionStoreMessageContentPart]] = None + """ + If a content parts array was provided, this is an array of `text` and + `image_url` parts. Otherwise, null. + """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 191793c18f..20d7c187f8 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -177,6 +177,13 @@ class CompletionCreateParamsBase(TypedDict, total=False): far, increasing the model's likelihood to talk about new topics. """ + prompt_cache_key: str + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + reasoning_effort: Optional[ReasoningEffort] """**o-series models only** @@ -199,6 +206,15 @@ class CompletionCreateParamsBase(TypedDict, total=False): preferred for models that support it. """ + safety_identifier: str + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + seed: Optional[int] """ This feature is in Beta. If specified, our system will make a best effort to @@ -293,11 +309,12 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ user: str - """A stable identifier for your end-users. + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. - Used to boost cache hit rates by better bucketing similar requests and to help - OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ web_search_options: WebSearchOptions diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 2af85d03fb..7db466dfe7 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -163,6 +163,13 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + reasoning: Optional[Reasoning] = None """**o-series models only** @@ -170,6 +177,15 @@ class Response(BaseModel): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] = None """Specifies the processing type used for serving the request. @@ -229,11 +245,12 @@ class Response(BaseModel): """ user: Optional[str] = None - """A stable identifier for your end-users. + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. - Used to boost cache hit rates by better bucketing similar requests and to help - OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ @property diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 08feefd081..4a78d7c028 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -123,6 +123,13 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + prompt_cache_key: str + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + reasoning: Optional[Reasoning] """**o-series models only** @@ -130,6 +137,15 @@ class ResponseCreateParamsBase(TypedDict, total=False): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ + safety_identifier: str + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user. We recommend hashing their username or email address, in + order to avoid sending us any identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] """Specifies the processing type used for serving the request. @@ -221,11 +237,12 @@ class ResponseCreateParamsBase(TypedDict, total=False): """ user: str - """A stable identifier for your end-users. + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. - Used to boost cache hit rates by better bucketing similar requests and to help - OpenAI detect and prevent abuse. - [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids). + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index aa8f58f0e5..2758d980ed 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -72,8 +72,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "type": "content", }, presence_penalty=-2, + prompt_cache_key="prompt-cache-key-1234", reasoning_effort="low", response_format={"type": "text"}, + safety_identifier="safety-identifier-1234", seed=-9007199254740991, service_tier="auto", stop="\n", @@ -199,8 +201,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "type": "content", }, presence_penalty=-2, + prompt_cache_key="prompt-cache-key-1234", reasoning_effort="low", response_format={"type": "text"}, + safety_identifier="safety-identifier-1234", seed=-9007199254740991, service_tier="auto", stop="\n", @@ -501,8 +505,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "type": "content", }, presence_penalty=-2, + prompt_cache_key="prompt-cache-key-1234", reasoning_effort="low", response_format={"type": "text"}, + safety_identifier="safety-identifier-1234", seed=-9007199254740991, service_tier="auto", stop="\n", @@ -628,8 +634,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "type": "content", }, presence_penalty=-2, + prompt_cache_key="prompt-cache-key-1234", reasoning_effort="low", response_format={"type": "text"}, + safety_identifier="safety-identifier-1234", seed=-9007199254740991, service_tier="auto", stop="\n", diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 158654ee70..63e47d8a69 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -43,11 +43,13 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "variables": {"foo": "string"}, "version": "version", }, + prompt_cache_key="prompt-cache-key-1234", reasoning={ "effort": "low", "generate_summary": "auto", "summary": "auto", }, + safety_identifier="safety-identifier-1234", service_tier="auto", store=True, stream=False, @@ -116,11 +118,13 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "variables": {"foo": "string"}, "version": "version", }, + prompt_cache_key="prompt-cache-key-1234", reasoning={ "effort": "low", "generate_summary": "auto", "summary": "auto", }, + safety_identifier="safety-identifier-1234", service_tier="auto", store=True, temperature=1, @@ -380,11 +384,13 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "variables": {"foo": "string"}, "version": "version", }, + prompt_cache_key="prompt-cache-key-1234", reasoning={ "effort": "low", "generate_summary": "auto", "summary": "auto", }, + safety_identifier="safety-identifier-1234", service_tier="auto", store=True, stream=False, @@ -453,11 +459,13 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "variables": {"foo": "string"}, "version": "version", }, + prompt_cache_key="prompt-cache-key-1234", reasoning={ "effort": "low", "generate_summary": "auto", "summary": "auto", }, + safety_identifier="safety-identifier-1234", service_tier="auto", store=True, temperature=1, From b204d41e0f1430b23207bbc2b809fa39e17b3564 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 30 Jul 2025 16:49:47 +0100 Subject: [PATCH 027/408] fix: add missing prompt_cache_key & prompt_cache_key params --- .../resources/chat/completions/completions.py | 16 ++++++++++++++++ src/openai/resources/responses/responses.py | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index c851851418..cd1cb2bd7f 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -101,7 +101,9 @@ def parse( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -197,8 +199,10 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), + "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, "stop": stop, @@ -1378,7 +1382,9 @@ def stream( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -1445,7 +1451,9 @@ def stream( parallel_tool_calls=parallel_tool_calls, prediction=prediction, presence_penalty=presence_penalty, + prompt_cache_key=prompt_cache_key, reasoning_effort=reasoning_effort, + safety_identifier=safety_identifier, seed=seed, service_tier=service_tier, store=store, @@ -1514,7 +1522,9 @@ async def parse( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -1610,8 +1620,10 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, "presence_penalty": presence_penalty, + "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), + "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, "store": store, @@ -2791,7 +2803,9 @@ def stream( parallel_tool_calls: bool | NotGiven = NOT_GIVEN, prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, @@ -2859,7 +2873,9 @@ def stream( parallel_tool_calls=parallel_tool_calls, prediction=prediction, presence_penalty=presence_penalty, + prompt_cache_key=prompt_cache_key, reasoning_effort=reasoning_effort, + safety_identifier=safety_identifier, seed=seed, service_tier=service_tier, stop=stop, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 8de46dbab8..6d2b133110 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1001,7 +1001,9 @@ def parse( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -1053,7 +1055,9 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, + "prompt_cache_key": prompt_cache_key, "reasoning": reasoning, + "safety_identifier": safety_identifier, "service_tier": service_tier, "store": store, "stream": stream, @@ -2316,7 +2320,9 @@ async def parse( parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, @@ -2368,7 +2374,9 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, + "prompt_cache_key": prompt_cache_key, "reasoning": reasoning, + "safety_identifier": safety_identifier, "service_tier": service_tier, "store": store, "stream": stream, From b989e8c240533d7003d479a947bb1733df84fe71 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:02:41 +0000 Subject: [PATCH 028/408] feat(client): support file upload requests --- src/openai/_base_client.py | 5 ++++- src/openai/_files.py | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 3fe669259f..f71e00f51f 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -534,7 +534,10 @@ def _build_request( is_body_allowed = options.method.lower() != "get" if is_body_allowed: - kwargs["json"] = json_data if is_given(json_data) else None + if isinstance(json_data, bytes): + kwargs["content"] = json_data + else: + kwargs["json"] = json_data if is_given(json_data) else None kwargs["files"] = files else: headers.pop("Content-Type", None) diff --git a/src/openai/_files.py b/src/openai/_files.py index 801a0d2928..7b23ca084a 100644 --- a/src/openai/_files.py +++ b/src/openai/_files.py @@ -69,12 +69,12 @@ def _transform_file(file: FileTypes) -> HttpxFileTypes: return file if is_tuple_t(file): - return (file[0], _read_file_content(file[1]), *file[2:]) + return (file[0], read_file_content(file[1]), *file[2:]) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") -def _read_file_content(file: FileContent) -> HttpxFileContent: +def read_file_content(file: FileContent) -> HttpxFileContent: if isinstance(file, os.PathLike): return pathlib.Path(file).read_bytes() return file @@ -111,12 +111,12 @@ async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: return file if is_tuple_t(file): - return (file[0], await _async_read_file_content(file[1]), *file[2:]) + return (file[0], await async_read_file_content(file[1]), *file[2:]) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") -async def _async_read_file_content(file: FileContent) -> HttpxFileContent: +async def async_read_file_content(file: FileContent) -> HttpxFileContent: if isinstance(file, os.PathLike): return await anyio.Path(file).read_bytes() From 29ce19fcf98027c4e17f449666f62f3e8fce3486 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:23:42 +0000 Subject: [PATCH 029/408] chore(internal): fix ruff target version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6765611fc2..a495edc1a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -177,7 +177,7 @@ reportPrivateUsage = false [tool.ruff] line-length = 120 output-format = "grouped" -target-version = "py37" +target-version = "py38" [tool.ruff.format] docstring-code-format = true From 2026d53339e61bfd5134e835bce6187baaca5b04 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 16:50:43 +0000 Subject: [PATCH 030/408] feat(api): manual updates --- .stats.yml | 6 ++-- api.md | 4 +-- src/openai/types/responses/__init__.py | 8 ++--- .../responses/response_reasoning_item.py | 19 ++++++++---- .../response_reasoning_item_param.py | 19 ++++++++---- .../response_reasoning_summary_delta_event.py | 30 ------------------- .../response_reasoning_summary_done_event.py | 27 ----------------- .../response_reasoning_text_delta_event.py | 27 +++++++++++++++++ .../response_reasoning_text_done_event.py | 27 +++++++++++++++++ .../types/responses/response_stream_event.py | 8 ++--- .../types/vector_store_search_params.py | 3 +- tests/api_resources/test_vector_stores.py | 4 +-- 12 files changed, 97 insertions(+), 85 deletions(-) delete mode 100644 src/openai/types/responses/response_reasoning_summary_delta_event.py delete mode 100644 src/openai/types/responses/response_reasoning_summary_done_event.py create mode 100644 src/openai/types/responses/response_reasoning_text_delta_event.py create mode 100644 src/openai/types/responses/response_reasoning_text_done_event.py diff --git a/.stats.yml b/.stats.yml index e7fb0bdf9b..f86fa668b1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-721e6ccaa72205ee14c71f8163129920464fb814b95d3df9567a9476bbd9b7fb.yml -openapi_spec_hash: 2115413a21df8b5bf9e4552a74df4312 -config_hash: 9606bb315a193bfd8da0459040143242 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d6a16b25b969c3e5382e7d413de15bf83d5f7534d5c3ecce64d3a7e847418f9e.yml +openapi_spec_hash: 0c0bcf4aee9ca2a948dd14b890dfe728 +config_hash: aeff9289bd7f8c8482e4d738c3c2fde1 diff --git a/api.md b/api.md index 0280b886d1..657ac0905a 100644 --- a/api.md +++ b/api.md @@ -792,12 +792,12 @@ from openai.types.responses import ( ResponsePrompt, ResponseQueuedEvent, ResponseReasoningItem, - ResponseReasoningSummaryDeltaEvent, - ResponseReasoningSummaryDoneEvent, ResponseReasoningSummaryPartAddedEvent, ResponseReasoningSummaryPartDoneEvent, ResponseReasoningSummaryTextDeltaEvent, ResponseReasoningSummaryTextDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, ResponseRefusalDeltaEvent, ResponseRefusalDoneEvent, ResponseStatus, diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index b563035e78..2e502ed69f 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -94,24 +94,20 @@ from .response_function_tool_call_param import ResponseFunctionToolCallParam as ResponseFunctionToolCallParam from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent as ResponseMcpCallCompletedEvent from .response_function_web_search_param import ResponseFunctionWebSearchParam as ResponseFunctionWebSearchParam +from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent as ResponseReasoningTextDoneEvent from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall as ResponseCodeInterpreterToolCall from .response_input_message_content_list import ResponseInputMessageContentList as ResponseInputMessageContentList from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent as ResponseMcpCallInProgressEvent +from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent as ResponseReasoningTextDeltaEvent from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam as ResponseFileSearchToolCallParam from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent as ResponseMcpListToolsFailedEvent from .response_audio_transcript_delta_event import ( ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, ) -from .response_reasoning_summary_done_event import ( - ResponseReasoningSummaryDoneEvent as ResponseReasoningSummaryDoneEvent, -) from .response_mcp_call_arguments_done_event import ( ResponseMcpCallArgumentsDoneEvent as ResponseMcpCallArgumentsDoneEvent, ) -from .response_reasoning_summary_delta_event import ( - ResponseReasoningSummaryDeltaEvent as ResponseReasoningSummaryDeltaEvent, -) from .response_computer_tool_call_output_item import ( ResponseComputerToolCallOutputItem as ResponseComputerToolCallOutputItem, ) diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py index f5da7802f8..e5cb094e62 100644 --- a/src/openai/types/responses/response_reasoning_item.py +++ b/src/openai/types/responses/response_reasoning_item.py @@ -5,29 +5,38 @@ from ..._models import BaseModel -__all__ = ["ResponseReasoningItem", "Summary"] +__all__ = ["ResponseReasoningItem", "Summary", "Content"] class Summary(BaseModel): text: str - """ - A short summary of the reasoning used by the model when generating the response. - """ + """A summary of the reasoning output from the model so far.""" type: Literal["summary_text"] """The type of the object. Always `summary_text`.""" +class Content(BaseModel): + text: str + """Reasoning text output from the model.""" + + type: Literal["reasoning_text"] + """The type of the object. Always `reasoning_text`.""" + + class ResponseReasoningItem(BaseModel): id: str """The unique identifier of the reasoning content.""" summary: List[Summary] - """Reasoning text contents.""" + """Reasoning summary content.""" type: Literal["reasoning"] """The type of the object. Always `reasoning`.""" + content: Optional[List[Content]] = None + """Reasoning text content.""" + encrypted_content: Optional[str] = None """ The encrypted content of the reasoning item - populated when a response is diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py index 2cfa5312ed..042b6c05db 100644 --- a/src/openai/types/responses/response_reasoning_item_param.py +++ b/src/openai/types/responses/response_reasoning_item_param.py @@ -5,29 +5,38 @@ from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["ResponseReasoningItemParam", "Summary"] +__all__ = ["ResponseReasoningItemParam", "Summary", "Content"] class Summary(TypedDict, total=False): text: Required[str] - """ - A short summary of the reasoning used by the model when generating the response. - """ + """A summary of the reasoning output from the model so far.""" type: Required[Literal["summary_text"]] """The type of the object. Always `summary_text`.""" +class Content(TypedDict, total=False): + text: Required[str] + """Reasoning text output from the model.""" + + type: Required[Literal["reasoning_text"]] + """The type of the object. Always `reasoning_text`.""" + + class ResponseReasoningItemParam(TypedDict, total=False): id: Required[str] """The unique identifier of the reasoning content.""" summary: Required[Iterable[Summary]] - """Reasoning text contents.""" + """Reasoning summary content.""" type: Required[Literal["reasoning"]] """The type of the object. Always `reasoning`.""" + content: Iterable[Content] + """Reasoning text content.""" + encrypted_content: Optional[str] """ The encrypted content of the reasoning item - populated when a response is diff --git a/src/openai/types/responses/response_reasoning_summary_delta_event.py b/src/openai/types/responses/response_reasoning_summary_delta_event.py deleted file mode 100644 index 519a4f24ac..0000000000 --- a/src/openai/types/responses/response_reasoning_summary_delta_event.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ResponseReasoningSummaryDeltaEvent"] - - -class ResponseReasoningSummaryDeltaEvent(BaseModel): - delta: object - """The partial update to the reasoning summary content.""" - - item_id: str - """ - The unique identifier of the item for which the reasoning summary is being - updated. - """ - - output_index: int - """The index of the output item in the response's output array.""" - - sequence_number: int - """The sequence number of this event.""" - - summary_index: int - """The index of the summary part within the output item.""" - - type: Literal["response.reasoning_summary.delta"] - """The type of the event. Always 'response.reasoning_summary.delta'.""" diff --git a/src/openai/types/responses/response_reasoning_summary_done_event.py b/src/openai/types/responses/response_reasoning_summary_done_event.py deleted file mode 100644 index 98bcf9cb9d..0000000000 --- a/src/openai/types/responses/response_reasoning_summary_done_event.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ResponseReasoningSummaryDoneEvent"] - - -class ResponseReasoningSummaryDoneEvent(BaseModel): - item_id: str - """The unique identifier of the item for which the reasoning summary is finalized.""" - - output_index: int - """The index of the output item in the response's output array.""" - - sequence_number: int - """The sequence number of this event.""" - - summary_index: int - """The index of the summary part within the output item.""" - - text: str - """The finalized reasoning summary text.""" - - type: Literal["response.reasoning_summary.done"] - """The type of the event. Always 'response.reasoning_summary.done'.""" diff --git a/src/openai/types/responses/response_reasoning_text_delta_event.py b/src/openai/types/responses/response_reasoning_text_delta_event.py new file mode 100644 index 0000000000..e1df893bac --- /dev/null +++ b/src/openai/types/responses/response_reasoning_text_delta_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningTextDeltaEvent"] + + +class ResponseReasoningTextDeltaEvent(BaseModel): + content_index: int + """The index of the reasoning content part this delta is associated with.""" + + delta: str + """The text delta that was added to the reasoning content.""" + + item_id: str + """The ID of the item this reasoning text delta is associated with.""" + + output_index: int + """The index of the output item this reasoning text delta is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.reasoning_text.delta"] + """The type of the event. Always `response.reasoning_text.delta`.""" diff --git a/src/openai/types/responses/response_reasoning_text_done_event.py b/src/openai/types/responses/response_reasoning_text_done_event.py new file mode 100644 index 0000000000..d22d984e47 --- /dev/null +++ b/src/openai/types/responses/response_reasoning_text_done_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseReasoningTextDoneEvent"] + + +class ResponseReasoningTextDoneEvent(BaseModel): + content_index: int + """The index of the reasoning content part.""" + + item_id: str + """The ID of the item this reasoning text is associated with.""" + + output_index: int + """The index of the output item this reasoning text is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + text: str + """The full text of the completed reasoning content.""" + + type: Literal["response.reasoning_text.done"] + """The type of the event. Always `response.reasoning_text.done`.""" diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py index 98e1d6c34d..d62cf8969b 100644 --- a/src/openai/types/responses/response_stream_event.py +++ b/src/openai/types/responses/response_stream_event.py @@ -23,13 +23,13 @@ from .response_output_item_added_event import ResponseOutputItemAddedEvent from .response_content_part_added_event import ResponseContentPartAddedEvent from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent +from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent +from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent -from .response_reasoning_summary_done_event import ResponseReasoningSummaryDoneEvent from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent -from .response_reasoning_summary_delta_event import ResponseReasoningSummaryDeltaEvent from .response_image_gen_call_completed_event import ResponseImageGenCallCompletedEvent from .response_mcp_call_arguments_delta_event import ResponseMcpCallArgumentsDeltaEvent from .response_mcp_list_tools_completed_event import ResponseMcpListToolsCompletedEvent @@ -88,6 +88,8 @@ ResponseReasoningSummaryPartDoneEvent, ResponseReasoningSummaryTextDeltaEvent, ResponseReasoningSummaryTextDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, ResponseRefusalDeltaEvent, ResponseRefusalDoneEvent, ResponseTextDeltaEvent, @@ -109,8 +111,6 @@ ResponseMcpListToolsInProgressEvent, ResponseOutputTextAnnotationAddedEvent, ResponseQueuedEvent, - ResponseReasoningSummaryDeltaEvent, - ResponseReasoningSummaryDoneEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/vector_store_search_params.py b/src/openai/types/vector_store_search_params.py index 17573d0f61..973c49ff5a 100644 --- a/src/openai/types/vector_store_search_params.py +++ b/src/openai/types/vector_store_search_params.py @@ -35,6 +35,7 @@ class VectorStoreSearchParams(TypedDict, total=False): class RankingOptions(TypedDict, total=False): - ranker: Literal["auto", "default-2024-11-15"] + ranker: Literal["none", "auto", "default-2024-11-15"] + """Enable re-ranking; set to `none` to disable, which can help reduce latency.""" score_threshold: float diff --git a/tests/api_resources/test_vector_stores.py b/tests/api_resources/test_vector_stores.py index 5af95fec41..dffd2b1d07 100644 --- a/tests/api_resources/test_vector_stores.py +++ b/tests/api_resources/test_vector_stores.py @@ -243,7 +243,7 @@ def test_method_search_with_all_params(self, client: OpenAI) -> None: }, max_num_results=1, ranking_options={ - "ranker": "auto", + "ranker": "none", "score_threshold": 0, }, rewrite_query=True, @@ -511,7 +511,7 @@ async def test_method_search_with_all_params(self, async_client: AsyncOpenAI) -> }, max_num_results=1, ranking_options={ - "ranker": "auto", + "ranker": "none", "score_threshold": 0, }, rewrite_query=True, From b0ad27a67681f1b6fb473cc75c642efa1f4941d5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 16:51:15 +0000 Subject: [PATCH 031/408] release: 1.99.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d12300ea76..5c9b107c0d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.98.0" + ".": "1.99.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 669d5a5792..e7a49bcc9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.99.0 (2025-08-05) + +Full Changelog: [v1.98.0...v1.99.0](https://github.com/openai/openai-python/compare/v1.98.0...v1.99.0) + +### Features + +* **api:** manual updates ([d4aa726](https://github.com/openai/openai-python/commit/d4aa72602bf489ef270154b881b3967d497d4220)) +* **client:** support file upload requests ([0772e6e](https://github.com/openai/openai-python/commit/0772e6ed8310e15539610b003dd73f72f474ec0c)) + + +### Bug Fixes + +* add missing prompt_cache_key & prompt_cache_key params ([00b49ae](https://github.com/openai/openai-python/commit/00b49ae8d44ea396ac0536fc3ce4658fc669e2f5)) + + +### Chores + +* **internal:** fix ruff target version ([aa6b252](https://github.com/openai/openai-python/commit/aa6b252ae0f25f195dede15755e05dd2f542f42d)) + ## 1.98.0 (2025-07-30) Full Changelog: [v1.97.2...v1.98.0](https://github.com/openai/openai-python/compare/v1.97.2...v1.98.0) diff --git a/pyproject.toml b/pyproject.toml index a495edc1a8..5e0f1fe3ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.98.0" +version = "1.99.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ca890665bc..a5c9b3df71 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.98.0" # x-release-please-version +__version__ = "1.99.0" # x-release-please-version From fd2c3f12cf3574f92aab2877f2903e6756018867 Mon Sep 17 00:00:00 2001 From: David Meadows Date: Tue, 5 Aug 2025 14:08:26 -0400 Subject: [PATCH 032/408] fix(internal): correct event imports --- examples/image_stream.py | 2 +- src/openai/lib/streaming/responses/_events.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/image_stream.py b/examples/image_stream.py index c188e68717..eab5932534 100644 --- a/examples/image_stream.py +++ b/examples/image_stream.py @@ -50,4 +50,4 @@ def main() -> None: try: main() except Exception as error: - print(f"Error generating image: {error}") \ No newline at end of file + print(f"Error generating image: {error}") diff --git a/src/openai/lib/streaming/responses/_events.py b/src/openai/lib/streaming/responses/_events.py index 4c8a588944..de3342ec9d 100644 --- a/src/openai/lib/streaming/responses/_events.py +++ b/src/openai/lib/streaming/responses/_events.py @@ -31,11 +31,9 @@ ResponseAudioTranscriptDoneEvent, ResponseAudioTranscriptDeltaEvent, ResponseMcpCallArgumentsDoneEvent, - ResponseReasoningSummaryDoneEvent, ResponseImageGenCallCompletedEvent, ResponseMcpCallArgumentsDeltaEvent, ResponseMcpListToolsCompletedEvent, - ResponseReasoningSummaryDeltaEvent, ResponseImageGenCallGeneratingEvent, ResponseImageGenCallInProgressEvent, ResponseMcpListToolsInProgressEvent, @@ -59,6 +57,8 @@ ResponseCodeInterpreterCallInProgressEvent, ResponseCodeInterpreterCallInterpretingEvent, ) +from ....types.responses.response_reasoning_text_done_event import ResponseReasoningTextDoneEvent +from ....types.responses.response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent TextFormatT = TypeVar( "TextFormatT", @@ -137,8 +137,8 @@ class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[Te ResponseMcpListToolsInProgressEvent, ResponseOutputTextAnnotationAddedEvent, ResponseQueuedEvent, - ResponseReasoningSummaryDeltaEvent, - ResponseReasoningSummaryDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, ], PropertyInfo(discriminator="type"), ] From a8258744cbecf51321587fc870e8920bd2c07809 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 18:08:59 +0000 Subject: [PATCH 033/408] release: 1.99.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5c9b107c0d..41be9f1017 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.0" + ".": "1.99.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e7a49bcc9a..4585135511 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.99.1 (2025-08-05) + +Full Changelog: [v1.99.0...v1.99.1](https://github.com/openai/openai-python/compare/v1.99.0...v1.99.1) + +### Bug Fixes + +* **internal:** correct event imports ([2a6d143](https://github.com/openai/openai-python/commit/2a6d1436288a07f67f6afefe5c0b5d6ae32d7e70)) + ## 1.99.0 (2025-08-05) Full Changelog: [v1.98.0...v1.99.0](https://github.com/openai/openai-python/compare/v1.98.0...v1.99.0) diff --git a/pyproject.toml b/pyproject.toml index 5e0f1fe3ea..c71e8c135b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.0" +version = "1.99.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a5c9b3df71..3fa80adba0 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.0" # x-release-please-version +__version__ = "1.99.1" # x-release-please-version From 936b2f0db2812c74c966a657d45acd972d2fd088 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 7 Aug 2025 10:58:11 +0100 Subject: [PATCH 034/408] chore(tests): bump inline-snapshot dependency --- requirements-dev.lock | 25 +++++--------------- tests/lib/chat/_utils.py | 12 ++++++++++ tests/lib/chat/test_completions.py | 6 ++--- tests/lib/chat/test_completions_streaming.py | 12 ++++++---- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 1a7500d569..b1886e036f 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -35,8 +35,6 @@ attrs==24.2.0 azure-core==1.31.0 # via azure-identity azure-identity==1.19.0 -black==24.10.0 - # via inline-snapshot certifi==2023.7.22 # via httpcore # via httpx @@ -46,9 +44,6 @@ cffi==1.16.0 # via sounddevice charset-normalizer==3.3.2 # via requests -click==8.1.7 - # via black - # via inline-snapshot colorlog==6.7.0 # via nox cryptography==42.0.7 @@ -66,7 +61,7 @@ exceptiongroup==1.2.2 # via trio execnet==2.1.1 # via pytest-xdist -executing==2.1.0 +executing==2.2.0 # via inline-snapshot filelock==3.12.4 # via virtualenv @@ -92,7 +87,7 @@ idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest -inline-snapshot==0.10.2 +inline-snapshot==0.27.0 jiter==0.5.0 # via openai markdown-it-py==3.0.0 @@ -109,7 +104,6 @@ multidict==6.5.0 # via yarl mypy==1.14.1 mypy-extensions==1.0.0 - # via black # via mypy nest-asyncio==1.6.0 nodeenv==1.8.0 @@ -122,17 +116,13 @@ numpy==2.0.2 outcome==1.3.0.post0 # via trio packaging==23.2 - # via black # via nox # via pytest pandas==2.2.3 # via openai pandas-stubs==2.1.4.231227 # via openai -pathspec==0.12.1 - # via black platformdirs==3.11.0 - # via black # via virtualenv pluggy==1.5.0 # via pytest @@ -148,11 +138,13 @@ pydantic==2.10.3 pydantic-core==2.27.1 # via pydantic pygments==2.18.0 + # via pytest # via rich pyjwt==2.8.0 # via msal pyright==1.1.399 -pytest==8.3.3 +pytest==8.4.1 + # via inline-snapshot # via pytest-asyncio # via pytest-xdist pytest-asyncio==0.24.0 @@ -185,10 +177,8 @@ sortedcontainers==2.4.0 sounddevice==0.5.1 # via openai time-machine==2.9.0 -toml==0.10.2 - # via inline-snapshot tomli==2.0.2 - # via black + # via inline-snapshot # via mypy # via pytest tqdm==4.66.5 @@ -197,13 +187,10 @@ trio==0.27.0 types-pyaudio==0.2.16.20240516 types-pytz==2024.2.0.20241003 # via pandas-stubs -types-toml==0.10.8.20240310 - # via inline-snapshot types-tqdm==4.66.0.20240417 typing-extensions==4.12.2 # via azure-core # via azure-identity - # via black # via multidict # via mypy # via openai diff --git a/tests/lib/chat/_utils.py b/tests/lib/chat/_utils.py index f3982278f3..0cc1c99952 100644 --- a/tests/lib/chat/_utils.py +++ b/tests/lib/chat/_utils.py @@ -52,3 +52,15 @@ def get_caller_name(*, stacklevel: int = 1) -> str: def clear_locals(string: str, *, stacklevel: int) -> str: caller = get_caller_name(stacklevel=stacklevel + 1) return string.replace(f"{caller}..", "") + + +def get_snapshot_value(snapshot: Any) -> Any: + if not hasattr(snapshot, "_old_value"): + return snapshot + + old = snapshot._old_value + if not hasattr(old, "value"): + return old + + loader = getattr(old.value, "_load_value", None) + return loader() if loader else old.value diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index e7143bbb68..d0bd14ce9e 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -17,7 +17,7 @@ from openai._utils import assert_signatures_in_sync from openai._compat import PYDANTIC_V2 -from ._utils import print_obj +from ._utils import print_obj, get_snapshot_value from ...conftest import base_url from ..schema_types.query import Query @@ -1010,7 +1010,7 @@ def _on_response(response: httpx.Response) -> None: respx_mock.post("/chat/completions").mock( return_value=httpx.Response( 200, - content=content_snapshot._old_value, + content=get_snapshot_value(content_snapshot), headers={"content-type": "application/json"}, ) ) @@ -1052,7 +1052,7 @@ async def _on_response(response: httpx.Response) -> None: respx_mock.post("/chat/completions").mock( return_value=httpx.Response( 200, - content=content_snapshot._old_value, + content=get_snapshot_value(content_snapshot), headers={"content-type": "application/json"}, ) ) diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 4680a73e3a..1daa98c6a0 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -9,7 +9,11 @@ import pytest from respx import MockRouter from pydantic import BaseModel -from inline_snapshot import external, snapshot, outsource +from inline_snapshot import ( + external, + snapshot, + outsource, # pyright: ignore[reportUnknownVariableType] +) import openai from openai import OpenAI, AsyncOpenAI @@ -26,7 +30,7 @@ ) from openai.lib._parsing._completions import ResponseFormatT -from ._utils import print_obj +from ._utils import print_obj, get_snapshot_value from ...conftest import base_url _T = TypeVar("_T") @@ -1123,7 +1127,7 @@ def _on_response(response: httpx.Response) -> None: respx_mock.post("/chat/completions").mock( return_value=httpx.Response( 200, - content=content_snapshot._old_value._load_value(), + content=get_snapshot_value(content_snapshot), headers={"content-type": "text/event-stream"}, ) ) @@ -1170,7 +1174,7 @@ def _on_response(response: httpx.Response) -> None: respx_mock.post("/chat/completions").mock( return_value=httpx.Response( 200, - content=content_snapshot._old_value._load_value(), + content=get_snapshot_value(content_snapshot), headers={"content-type": "text/event-stream"}, ) ) From caf837bb89a107e3658e56190b03f246ee23b917 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 17:02:31 +0000 Subject: [PATCH 035/408] feat(api): adds GPT-5 and new API features: platform.openai.com/docs/guides/gpt-5 --- .stats.yml | 6 +- api.md | 17 ++ src/openai/lib/_parsing/_completions.py | 57 ++++++- src/openai/lib/_parsing/_responses.py | 1 + src/openai/lib/_tools.py | 4 +- src/openai/lib/streaming/chat/_completions.py | 3 +- src/openai/resources/beta/assistants.py | 48 +++--- .../resources/beta/threads/runs/runs.py | 54 +++---- .../resources/chat/completions/completions.py | 138 ++++++++++------ src/openai/resources/responses/responses.py | 148 +++++++++++++++++- src/openai/types/__init__.py | 3 + .../types/beta/assistant_create_params.py | 8 +- .../types/beta/assistant_update_params.py | 14 +- .../types/beta/threads/run_create_params.py | 8 +- src/openai/types/chat/__init__.py | 23 ++- ...at_completion_allowed_tool_choice_param.py | 17 ++ .../chat_completion_allowed_tools_param.py | 32 ++++ .../chat/chat_completion_custom_tool_param.py | 58 +++++++ ...ol.py => chat_completion_function_tool.py} | 4 +- .../chat_completion_function_tool_param.py | 16 ++ ...hat_completion_message_custom_tool_call.py | 26 +++ ...mpletion_message_custom_tool_call_param.py | 26 +++ ...t_completion_message_function_tool_call.py | 31 ++++ ...letion_message_function_tool_call_param.py | 31 ++++ .../chat/chat_completion_message_tool_call.py | 36 ++--- ...chat_completion_message_tool_call_param.py | 32 +--- ...mpletion_named_tool_choice_custom_param.py | 19 +++ ...chat_completion_named_tool_choice_param.py | 2 +- .../chat_completion_stream_options_param.py | 11 ++ ...hat_completion_tool_choice_option_param.py | 7 +- .../types/chat/chat_completion_tool_param.py | 13 +- .../types/chat/completion_create_params.py | 22 ++- .../types/chat/parsed_function_tool_call.py | 4 +- ...create_eval_completions_run_data_source.py | 4 +- ..._eval_completions_run_data_source_param.py | 4 +- src/openai/types/responses/__init__.py | 18 +++ src/openai/types/responses/custom_tool.py | 23 +++ .../types/responses/custom_tool_param.py | 23 +++ src/openai/types/responses/parsed_response.py | 2 + src/openai/types/responses/response.py | 29 ++-- .../types/responses/response_create_params.py | 44 +++++- .../responses/response_custom_tool_call.py | 25 +++ ...onse_custom_tool_call_input_delta_event.py | 24 +++ ...ponse_custom_tool_call_input_done_event.py | 24 +++ .../response_custom_tool_call_output.py | 22 +++ .../response_custom_tool_call_output_param.py | 21 +++ .../response_custom_tool_call_param.py | 24 +++ .../types/responses/response_input_item.py | 4 + .../responses/response_input_item_param.py | 4 + .../types/responses/response_input_param.py | 4 + .../types/responses/response_output_item.py | 2 + .../responses/response_retrieve_params.py | 11 ++ .../types/responses/response_stream_event.py | 4 + src/openai/types/responses/tool.py | 13 +- .../types/responses/tool_choice_allowed.py | 36 +++++ .../responses/tool_choice_allowed_param.py | 36 +++++ .../types/responses/tool_choice_custom.py | 15 ++ .../responses/tool_choice_custom_param.py | 15 ++ src/openai/types/responses/tool_param.py | 2 + src/openai/types/shared/__init__.py | 3 + src/openai/types/shared/chat_model.py | 7 + .../types/shared/custom_tool_input_format.py | 28 ++++ src/openai/types/shared/reasoning.py | 8 +- src/openai/types/shared/reasoning_effort.py | 2 +- .../shared/response_format_text_grammar.py | 15 ++ .../shared/response_format_text_python.py | 12 ++ src/openai/types/shared_params/__init__.py | 1 + src/openai/types/shared_params/chat_model.py | 7 + .../shared_params/custom_tool_input_format.py | 27 ++++ src/openai/types/shared_params/reasoning.py | 8 +- .../types/shared_params/reasoning_effort.py | 2 +- tests/api_resources/beta/test_assistants.py | 8 +- tests/api_resources/beta/threads/test_runs.py | 8 +- tests/api_resources/chat/test_completions.py | 32 +++- tests/api_resources/test_completions.py | 20 ++- tests/api_resources/test_responses.py | 20 ++- 76 files changed, 1293 insertions(+), 267 deletions(-) create mode 100644 src/openai/types/chat/chat_completion_allowed_tool_choice_param.py create mode 100644 src/openai/types/chat/chat_completion_allowed_tools_param.py create mode 100644 src/openai/types/chat/chat_completion_custom_tool_param.py rename src/openai/types/chat/{chat_completion_tool.py => chat_completion_function_tool.py} (80%) create mode 100644 src/openai/types/chat/chat_completion_function_tool_param.py create mode 100644 src/openai/types/chat/chat_completion_message_custom_tool_call.py create mode 100644 src/openai/types/chat/chat_completion_message_custom_tool_call_param.py create mode 100644 src/openai/types/chat/chat_completion_message_function_tool_call.py create mode 100644 src/openai/types/chat/chat_completion_message_function_tool_call_param.py create mode 100644 src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py create mode 100644 src/openai/types/responses/custom_tool.py create mode 100644 src/openai/types/responses/custom_tool_param.py create mode 100644 src/openai/types/responses/response_custom_tool_call.py create mode 100644 src/openai/types/responses/response_custom_tool_call_input_delta_event.py create mode 100644 src/openai/types/responses/response_custom_tool_call_input_done_event.py create mode 100644 src/openai/types/responses/response_custom_tool_call_output.py create mode 100644 src/openai/types/responses/response_custom_tool_call_output_param.py create mode 100644 src/openai/types/responses/response_custom_tool_call_param.py create mode 100644 src/openai/types/responses/tool_choice_allowed.py create mode 100644 src/openai/types/responses/tool_choice_allowed_param.py create mode 100644 src/openai/types/responses/tool_choice_custom.py create mode 100644 src/openai/types/responses/tool_choice_custom_param.py create mode 100644 src/openai/types/shared/custom_tool_input_format.py create mode 100644 src/openai/types/shared/response_format_text_grammar.py create mode 100644 src/openai/types/shared/response_format_text_python.py create mode 100644 src/openai/types/shared_params/custom_tool_input_format.py diff --git a/.stats.yml b/.stats.yml index f86fa668b1..9c1b4e4c54 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d6a16b25b969c3e5382e7d413de15bf83d5f7534d5c3ecce64d3a7e847418f9e.yml -openapi_spec_hash: 0c0bcf4aee9ca2a948dd14b890dfe728 -config_hash: aeff9289bd7f8c8482e4d738c3c2fde1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f5c45f4ae5c2075cbc603d6910bba3da31c23714c209fbd3fd82a94f634a126b.yml +openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba +config_hash: 9a64321968e21ed72f5c0e02164ea00d diff --git a/api.md b/api.md index 657ac0905a..f05b3f61ee 100644 --- a/api.md +++ b/api.md @@ -6,6 +6,7 @@ from openai.types import ( ChatModel, ComparisonFilter, CompoundFilter, + CustomToolInputFormat, ErrorObject, FunctionDefinition, FunctionParameters, @@ -15,6 +16,8 @@ from openai.types import ( ResponseFormatJSONObject, ResponseFormatJSONSchema, ResponseFormatText, + ResponseFormatTextGrammar, + ResponseFormatTextPython, ResponsesModel, ) ``` @@ -46,6 +49,7 @@ Types: ```python from openai.types.chat import ( ChatCompletion, + ChatCompletionAllowedToolChoice, ChatCompletionAssistantMessageParam, ChatCompletionAudio, ChatCompletionAudioParam, @@ -55,15 +59,20 @@ from openai.types.chat import ( ChatCompletionContentPartInputAudio, ChatCompletionContentPartRefusal, ChatCompletionContentPartText, + ChatCompletionCustomTool, ChatCompletionDeleted, ChatCompletionDeveloperMessageParam, ChatCompletionFunctionCallOption, ChatCompletionFunctionMessageParam, + ChatCompletionFunctionTool, ChatCompletionMessage, + ChatCompletionMessageCustomToolCall, + ChatCompletionMessageFunctionToolCall, ChatCompletionMessageParam, ChatCompletionMessageToolCall, ChatCompletionModality, ChatCompletionNamedToolChoice, + ChatCompletionNamedToolChoiceCustom, ChatCompletionPredictionContent, ChatCompletionRole, ChatCompletionStoreMessage, @@ -74,6 +83,7 @@ from openai.types.chat import ( ChatCompletionToolChoiceOption, ChatCompletionToolMessageParam, ChatCompletionUserMessageParam, + ChatCompletionAllowedTools, ChatCompletionReasoningEffort, ) ``` @@ -719,6 +729,7 @@ Types: ```python from openai.types.responses import ( ComputerTool, + CustomTool, EasyInputMessage, FileSearchTool, FunctionTool, @@ -741,6 +752,10 @@ from openai.types.responses import ( ResponseContentPartAddedEvent, ResponseContentPartDoneEvent, ResponseCreatedEvent, + ResponseCustomToolCall, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, + ResponseCustomToolCallOutput, ResponseError, ResponseErrorEvent, ResponseFailedEvent, @@ -810,6 +825,8 @@ from openai.types.responses import ( ResponseWebSearchCallInProgressEvent, ResponseWebSearchCallSearchingEvent, Tool, + ToolChoiceAllowed, + ToolChoiceCustom, ToolChoiceFunction, ToolChoiceMcp, ToolChoiceOptions, diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index c160070b66..e14c33864d 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import logging from typing import TYPE_CHECKING, Any, Iterable, cast from typing_extensions import TypeVar, TypeGuard, assert_never @@ -19,14 +20,15 @@ ParsedChatCompletion, ChatCompletionMessage, ParsedFunctionToolCall, - ChatCompletionToolParam, ParsedChatCompletionMessage, + ChatCompletionFunctionToolParam, completion_create_params, ) from ..._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError from ...types.shared_params import FunctionDefinition from ...types.chat.completion_create_params import ResponseFormat as ResponseFormatParam -from ...types.chat.chat_completion_message_tool_call import Function +from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam +from ...types.chat.chat_completion_message_function_tool_call import Function ResponseFormatT = TypeVar( "ResponseFormatT", @@ -35,12 +37,36 @@ ) _default_response_format: None = None +log: logging.Logger = logging.getLogger("openai.lib.parsing") + + +def is_strict_chat_completion_tool_param( + tool: ChatCompletionToolParam, +) -> TypeGuard[ChatCompletionFunctionToolParam]: + """Check if the given tool is a strict ChatCompletionFunctionToolParam.""" + if not tool["type"] == "function": + return False + if tool["function"].get("strict") is not True: + return False + + return True + + +def select_strict_chat_completion_tools( + tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, +) -> Iterable[ChatCompletionFunctionToolParam] | NotGiven: + """Select only the strict ChatCompletionFunctionToolParams from the given tools.""" + if not is_given(tools): + return NOT_GIVEN + + return [t for t in tools if is_strict_chat_completion_tool_param(t)] + def validate_input_tools( tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, -) -> None: +) -> Iterable[ChatCompletionFunctionToolParam] | NotGiven: if not is_given(tools): - return + return NOT_GIVEN for tool in tools: if tool["type"] != "function": @@ -54,6 +80,8 @@ def validate_input_tools( f"`{tool['function']['name']}` is not strict. Only `strict` function tools can be auto-parsed" ) + return cast(Iterable[ChatCompletionFunctionToolParam], tools) + def parse_chat_completion( *, @@ -95,6 +123,14 @@ def parse_chat_completion( type_=ParsedFunctionToolCall, ) ) + elif tool_call.type == "custom": + # warn user that custom tool calls are not callable here + log.warning( + "Custom tool calls are not callable. Ignoring tool call: %s - %s", + tool_call.id, + tool_call.custom.name, + stacklevel=2, + ) elif TYPE_CHECKING: # type: ignore[unreachable] assert_never(tool_call) else: @@ -129,13 +165,15 @@ def parse_chat_completion( ) -def get_input_tool_by_name(*, input_tools: list[ChatCompletionToolParam], name: str) -> ChatCompletionToolParam | None: - return next((t for t in input_tools if t.get("function", {}).get("name") == name), None) +def get_input_tool_by_name( + *, input_tools: list[ChatCompletionToolParam], name: str +) -> ChatCompletionFunctionToolParam | None: + return next((t for t in input_tools if t["type"] == "function" and t.get("function", {}).get("name") == name), None) def parse_function_tool_arguments( *, input_tools: list[ChatCompletionToolParam], function: Function | ParsedFunction -) -> object: +) -> object | None: input_tool = get_input_tool_by_name(input_tools=input_tools, name=function.name) if not input_tool: return None @@ -149,7 +187,7 @@ def parse_function_tool_arguments( if not input_fn.get("strict"): return None - return json.loads(function.arguments) + return json.loads(function.arguments) # type: ignore[no-any-return] def maybe_parse_content( @@ -209,6 +247,9 @@ def is_response_format_param(response_format: object) -> TypeGuard[ResponseForma def is_parseable_tool(input_tool: ChatCompletionToolParam) -> bool: + if input_tool["type"] != "function": + return False + input_fn = cast(object, input_tool.get("function")) if isinstance(input_fn, PydanticFunctionTool): return True diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 41be1d37b0..2a30ac836c 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -110,6 +110,7 @@ def parse_response( or output.type == "local_shell_call" or output.type == "mcp_list_tools" or output.type == "exec" + or output.type == "custom_tool_call" ): output_list.append(output) elif TYPE_CHECKING: # type: ignore diff --git a/src/openai/lib/_tools.py b/src/openai/lib/_tools.py index 415d750074..4070ad63bb 100644 --- a/src/openai/lib/_tools.py +++ b/src/openai/lib/_tools.py @@ -5,7 +5,7 @@ import pydantic from ._pydantic import to_strict_json_schema -from ..types.chat import ChatCompletionToolParam +from ..types.chat import ChatCompletionFunctionToolParam from ..types.shared_params import FunctionDefinition from ..types.responses.function_tool_param import FunctionToolParam as ResponsesFunctionToolParam @@ -42,7 +42,7 @@ def pydantic_function_tool( *, name: str | None = None, # inferred from class name by default description: str | None = None, # inferred from class docstring by default -) -> ChatCompletionToolParam: +) -> ChatCompletionFunctionToolParam: if description is None: # note: we intentionally don't use `.getdoc()` to avoid # including pydantic's docstrings diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index 2cf37efeae..1dff628a20 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -37,11 +37,12 @@ parse_function_tool_arguments, ) from ...._streaming import Stream, AsyncStream -from ....types.chat import ChatCompletionChunk, ParsedChatCompletion, ChatCompletionToolParam +from ....types.chat import ChatCompletionChunk, ParsedChatCompletion from ...._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError from ....types.chat.chat_completion import ChoiceLogprobs from ....types.chat.chat_completion_chunk import Choice as ChoiceChunk from ....types.chat.completion_create_params import ResponseFormat as ResponseFormatParam +from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam class ChatCompletionStream(Generic[ResponseFormatT]): diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 9059d93616..fe0c99c88a 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -96,12 +96,11 @@ def create( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -220,6 +219,12 @@ def update( model: Union[ str, Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", @@ -298,12 +303,11 @@ def update( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -545,12 +549,11 @@ async def create( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -669,6 +672,12 @@ async def update( model: Union[ str, Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", @@ -747,12 +756,11 @@ async def update( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 3d9ae9759e..01246d7c12 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -167,12 +167,11 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -322,12 +321,11 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -473,12 +471,11 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1600,12 +1597,11 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1755,12 +1751,11 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1906,12 +1901,11 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index cd1cb2bd7f..65f91396bd 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -115,6 +115,7 @@ def parse( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -165,7 +166,7 @@ class MathResponse(BaseModel): print("answer: ", message.parsed.final_answer) ``` """ - _validate_input_tools(tools) + chat_completion_tools = _validate_input_tools(tools) extra_headers = { "X-Stainless-Helper-Method": "chat.completions.parse", @@ -176,7 +177,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma return _parse_chat_completion( response_format=response_format, chat_completion=raw_completion, - input_tools=tools, + input_tools=chat_completion_tools, ) return self._post( @@ -215,6 +216,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "verbosity": verbosity, "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParams, @@ -268,6 +270,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -398,12 +401,11 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: An object specifying the format that the model must output. @@ -483,9 +485,9 @@ def create( `none` is the default when no tools are present. `auto` is the default if tools are present. - tools: A list of tools the model may call. Currently, only functions are supported as a - tool. Use this to provide a list of functions the model may generate JSON inputs - for. A max of 128 functions are supported. + tools: A list of tools the model may call. You can provide either + [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + or [function tools](https://platform.openai.com/docs/guides/function-calling). top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -503,6 +505,10 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). @@ -553,6 +559,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -692,12 +699,11 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: An object specifying the format that the model must output. @@ -768,9 +774,9 @@ def create( `none` is the default when no tools are present. `auto` is the default if tools are present. - tools: A list of tools the model may call. Currently, only functions are supported as a - tool. Use this to provide a list of functions the model may generate JSON inputs - for. A max of 128 functions are supported. + tools: A list of tools the model may call. You can provide either + [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + or [function tools](https://platform.openai.com/docs/guides/function-calling). top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -788,6 +794,10 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). @@ -838,6 +848,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -977,12 +988,11 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: An object specifying the format that the model must output. @@ -1053,9 +1063,9 @@ def create( `none` is the default when no tools are present. `auto` is the default if tools are present. - tools: A list of tools the model may call. Currently, only functions are supported as a - tool. Use this to provide a list of functions the model may generate JSON inputs - for. A max of 128 functions are supported. + tools: A list of tools the model may call. You can provide either + [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + or [function tools](https://platform.openai.com/docs/guides/function-calling). top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -1073,6 +1083,10 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). @@ -1123,6 +1137,7 @@ def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1168,6 +1183,7 @@ def create( "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "verbosity": verbosity, "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParamsStreaming @@ -1396,6 +1412,7 @@ def stream( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1465,6 +1482,7 @@ def stream( top_logprobs=top_logprobs, top_p=top_p, user=user, + verbosity=verbosity, web_search_options=web_search_options, extra_headers=extra_headers, extra_query=extra_query, @@ -1536,6 +1554,7 @@ async def parse( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1636,6 +1655,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "verbosity": verbosity, "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParams, @@ -1689,6 +1709,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1819,12 +1840,11 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: An object specifying the format that the model must output. @@ -1904,9 +1924,9 @@ async def create( `none` is the default when no tools are present. `auto` is the default if tools are present. - tools: A list of tools the model may call. Currently, only functions are supported as a - tool. Use this to provide a list of functions the model may generate JSON inputs - for. A max of 128 functions are supported. + tools: A list of tools the model may call. You can provide either + [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + or [function tools](https://platform.openai.com/docs/guides/function-calling). top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -1924,6 +1944,10 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). @@ -1974,6 +1998,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2113,12 +2138,11 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: An object specifying the format that the model must output. @@ -2189,9 +2213,9 @@ async def create( `none` is the default when no tools are present. `auto` is the default if tools are present. - tools: A list of tools the model may call. Currently, only functions are supported as a - tool. Use this to provide a list of functions the model may generate JSON inputs - for. A max of 128 functions are supported. + tools: A list of tools the model may call. You can provide either + [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + or [function tools](https://platform.openai.com/docs/guides/function-calling). top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -2209,6 +2233,10 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). @@ -2259,6 +2287,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2398,12 +2427,11 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning_effort: **o-series models only** - - Constrains effort on reasoning for + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. response_format: An object specifying the format that the model must output. @@ -2474,9 +2502,9 @@ async def create( `none` is the default when no tools are present. `auto` is the default if tools are present. - tools: A list of tools the model may call. Currently, only functions are supported as a - tool. Use this to provide a list of functions the model may generate JSON inputs - for. A max of 128 functions are supported. + tools: A list of tools the model may call. You can provide either + [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + or [function tools](https://platform.openai.com/docs/guides/function-calling). top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -2494,6 +2522,10 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). @@ -2544,6 +2576,7 @@ async def create( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2589,6 +2622,7 @@ async def create( "top_logprobs": top_logprobs, "top_p": top_p, "user": user, + "verbosity": verbosity, "web_search_options": web_search_options, }, completion_create_params.CompletionCreateParamsStreaming @@ -2817,6 +2851,7 @@ def stream( top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2887,11 +2922,12 @@ def stream( top_logprobs=top_logprobs, top_p=top_p, user=user, + verbosity=verbosity, + web_search_options=web_search_options, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - web_search_options=web_search_options, ) return AsyncChatCompletionStreamManager( api_request, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 6d2b133110..5ba22418ed 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -93,6 +93,7 @@ def create( service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -101,6 +102,7 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -232,6 +234,8 @@ def create( [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) for more information. + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or `top_p` but @@ -259,8 +263,10 @@ def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -285,6 +291,10 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -316,6 +326,7 @@ def create( safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -324,6 +335,7 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -455,6 +467,8 @@ def create( store: Whether to store the generated model response for later retrieval via API. + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or `top_p` but @@ -482,8 +496,10 @@ def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -508,6 +524,10 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -539,6 +559,7 @@ def create( safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -547,6 +568,7 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -678,6 +700,8 @@ def create( store: Whether to store the generated model response for later retrieval via API. + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or `top_p` but @@ -705,8 +729,10 @@ def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -731,6 +757,10 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -761,6 +791,7 @@ def create( service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -769,6 +800,7 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -797,6 +829,7 @@ def create( "service_tier": service_tier, "store": store, "stream": stream, + "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, @@ -805,6 +838,7 @@ def create( "top_p": top_p, "truncation": truncation, "user": user, + "verbosity": verbosity, }, response_create_params.ResponseCreateParamsStreaming if stream @@ -850,6 +884,7 @@ def stream( previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -881,6 +916,7 @@ def stream( previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -906,6 +942,7 @@ def stream( "previous_response_id": previous_response_id, "reasoning": reasoning, "store": store, + "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, @@ -950,6 +987,7 @@ def stream( parallel_tool_calls=parallel_tool_calls, previous_response_id=previous_response_id, store=store, + stream_options=stream_options, stream=True, temperature=temperature, text=text, @@ -1007,6 +1045,7 @@ def parse( service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -1015,6 +1054,7 @@ def parse( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1061,6 +1101,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "service_tier": service_tier, "store": store, "stream": stream, + "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, @@ -1069,6 +1110,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "top_p": top_p, "truncation": truncation, "user": user, + "verbosity": verbosity, }, response_create_params.ResponseCreateParams, ), @@ -1090,6 +1132,7 @@ def retrieve( response_id: str, *, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, stream: Literal[False] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1154,6 +1197,13 @@ def retrieve( include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + starting_after: The sequence number of the event after which to start streaming. stream: If set to true, the model response data will be streamed to the client as it is @@ -1180,6 +1230,7 @@ def retrieve( *, stream: Literal[True], include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1202,6 +1253,13 @@ def retrieve( include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + starting_after: The sequence number of the event after which to start streaming. extra_headers: Send extra headers @@ -1221,6 +1279,7 @@ def retrieve( *, stream: bool, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1243,6 +1302,13 @@ def retrieve( include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + starting_after: The sequence number of the event after which to start streaming. extra_headers: Send extra headers @@ -1260,6 +1326,7 @@ def retrieve( response_id: str, *, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1281,6 +1348,7 @@ def retrieve( query=maybe_transform( { "include": include, + "include_obfuscation": include_obfuscation, "starting_after": starting_after, "stream": stream, }, @@ -1408,6 +1476,7 @@ async def create( service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -1416,6 +1485,7 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1547,6 +1617,8 @@ async def create( [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) for more information. + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or `top_p` but @@ -1574,8 +1646,10 @@ async def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -1600,6 +1674,10 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1631,6 +1709,7 @@ async def create( safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -1639,6 +1718,7 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1770,6 +1850,8 @@ async def create( store: Whether to store the generated model response for later retrieval via API. + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or `top_p` but @@ -1797,8 +1879,10 @@ async def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -1823,6 +1907,10 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1854,6 +1942,7 @@ async def create( safety_identifier: str | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -1862,6 +1951,7 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1993,6 +2083,8 @@ async def create( store: Whether to store the generated model response for later retrieval via API. + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or `top_p` but @@ -2020,8 +2112,10 @@ async def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. @@ -2046,6 +2140,10 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + verbosity: Constrains the verbosity of the model's response. Lower values will result in + more concise responses, while higher values will result in more verbose + responses. Currently supported values are `low`, `medium`, and `high`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2076,6 +2174,7 @@ async def create( service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -2084,6 +2183,7 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2112,6 +2212,7 @@ async def create( "service_tier": service_tier, "store": store, "stream": stream, + "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, @@ -2120,6 +2221,7 @@ async def create( "top_p": top_p, "truncation": truncation, "user": user, + "verbosity": verbosity, }, response_create_params.ResponseCreateParamsStreaming if stream @@ -2165,6 +2267,7 @@ def stream( previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -2196,6 +2299,7 @@ def stream( previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -2221,6 +2325,7 @@ def stream( "previous_response_id": previous_response_id, "reasoning": reasoning, "store": store, + "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, @@ -2266,6 +2371,7 @@ def stream( parallel_tool_calls=parallel_tool_calls, previous_response_id=previous_response_id, store=store, + stream_options=stream_options, temperature=temperature, text=text, tool_choice=tool_choice, @@ -2326,6 +2432,7 @@ async def parse( service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, @@ -2334,6 +2441,7 @@ async def parse( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, + verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2380,6 +2488,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "service_tier": service_tier, "store": store, "stream": stream, + "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, @@ -2388,6 +2497,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "top_p": top_p, "truncation": truncation, "user": user, + "verbosity": verbosity, }, response_create_params.ResponseCreateParams, ), @@ -2409,6 +2519,7 @@ async def retrieve( response_id: str, *, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, stream: Literal[False] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2473,6 +2584,13 @@ async def retrieve( include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + starting_after: The sequence number of the event after which to start streaming. stream: If set to true, the model response data will be streamed to the client as it is @@ -2499,6 +2617,7 @@ async def retrieve( *, stream: Literal[True], include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2521,6 +2640,13 @@ async def retrieve( include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + starting_after: The sequence number of the event after which to start streaming. extra_headers: Send extra headers @@ -2540,6 +2666,7 @@ async def retrieve( *, stream: bool, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2562,6 +2689,13 @@ async def retrieve( include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + starting_after: The sequence number of the event after which to start streaming. extra_headers: Send extra headers @@ -2579,6 +2713,7 @@ async def retrieve( response_id: str, *, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include_obfuscation: bool | NotGiven = NOT_GIVEN, starting_after: int | NotGiven = NOT_GIVEN, stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2600,6 +2735,7 @@ async def retrieve( query=await async_maybe_transform( { "include": include, + "include_obfuscation": include_obfuscation, "starting_after": starting_after, "stream": stream, }, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 51f3ee5c9b..1844f71ba7 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -18,8 +18,11 @@ FunctionDefinition as FunctionDefinition, FunctionParameters as FunctionParameters, ResponseFormatText as ResponseFormatText, + CustomToolInputFormat as CustomToolInputFormat, ResponseFormatJSONObject as ResponseFormatJSONObject, ResponseFormatJSONSchema as ResponseFormatJSONSchema, + ResponseFormatTextPython as ResponseFormatTextPython, + ResponseFormatTextGrammar as ResponseFormatTextGrammar, ) from .upload import Upload as Upload from .embedding import Embedding as Embedding diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 8b3c331850..4b03dc0ea6 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -58,12 +58,12 @@ class AssistantCreateParams(TypedDict, total=False): """The name of the assistant. The maximum length is 256 characters.""" reasoning_effort: Optional[ReasoningEffort] - """**o-series models only** - + """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index b28094a6a5..e032554db8 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -36,6 +36,12 @@ class AssistantUpdateParams(TypedDict, total=False): model: Union[ str, Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", @@ -87,12 +93,12 @@ class AssistantUpdateParams(TypedDict, total=False): """The name of the assistant. The maximum length is 256 characters.""" reasoning_effort: Optional[ReasoningEffort] - """**o-series models only** - + """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index fc70227862..f9defcb19c 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -108,12 +108,12 @@ class RunCreateParamsBase(TypedDict, total=False): """ reasoning_effort: Optional[ReasoningEffort] - """**o-series models only** - + """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index dc26198567..ce1cf4522a 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -4,7 +4,6 @@ from .chat_completion import ChatCompletion as ChatCompletion from .chat_completion_role import ChatCompletionRole as ChatCompletionRole -from .chat_completion_tool import ChatCompletionTool as ChatCompletionTool from .chat_completion_audio import ChatCompletionAudio as ChatCompletionAudio from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk from .completion_list_params import CompletionListParams as CompletionListParams @@ -24,16 +23,20 @@ ) from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam as ChatCompletionAudioParam +from .chat_completion_function_tool import ChatCompletionFunctionTool as ChatCompletionFunctionTool from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam from .chat_completion_store_message import ChatCompletionStoreMessage as ChatCompletionStoreMessage from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort as ChatCompletionReasoningEffort from .chat_completion_content_part_text import ChatCompletionContentPartText as ChatCompletionContentPartText +from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam as ChatCompletionCustomToolParam from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall from .chat_completion_content_part_image import ChatCompletionContentPartImage as ChatCompletionContentPartImage from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam from .chat_completion_user_message_param import ChatCompletionUserMessageParam as ChatCompletionUserMessageParam +from .chat_completion_allowed_tools_param import ChatCompletionAllowedToolsParam as ChatCompletionAllowedToolsParam +from .chat_completion_function_tool_param import ChatCompletionFunctionToolParam as ChatCompletionFunctionToolParam from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam as ChatCompletionStreamOptionsParam from .chat_completion_system_message_param import ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam from .chat_completion_function_message_param import ( @@ -57,18 +60,36 @@ from .chat_completion_content_part_image_param import ( ChatCompletionContentPartImageParam as ChatCompletionContentPartImageParam, ) +from .chat_completion_message_custom_tool_call import ( + ChatCompletionMessageCustomToolCall as ChatCompletionMessageCustomToolCall, +) from .chat_completion_prediction_content_param import ( ChatCompletionPredictionContentParam as ChatCompletionPredictionContentParam, ) from .chat_completion_tool_choice_option_param import ( ChatCompletionToolChoiceOptionParam as ChatCompletionToolChoiceOptionParam, ) +from .chat_completion_allowed_tool_choice_param import ( + ChatCompletionAllowedToolChoiceParam as ChatCompletionAllowedToolChoiceParam, +) from .chat_completion_content_part_refusal_param import ( ChatCompletionContentPartRefusalParam as ChatCompletionContentPartRefusalParam, ) from .chat_completion_function_call_option_param import ( ChatCompletionFunctionCallOptionParam as ChatCompletionFunctionCallOptionParam, ) +from .chat_completion_message_function_tool_call import ( + ChatCompletionMessageFunctionToolCall as ChatCompletionMessageFunctionToolCall, +) from .chat_completion_content_part_input_audio_param import ( ChatCompletionContentPartInputAudioParam as ChatCompletionContentPartInputAudioParam, ) +from .chat_completion_message_custom_tool_call_param import ( + ChatCompletionMessageCustomToolCallParam as ChatCompletionMessageCustomToolCallParam, +) +from .chat_completion_named_tool_choice_custom_param import ( + ChatCompletionNamedToolChoiceCustomParam as ChatCompletionNamedToolChoiceCustomParam, +) +from .chat_completion_message_function_tool_call_param import ( + ChatCompletionMessageFunctionToolCallParam as ChatCompletionMessageFunctionToolCallParam, +) diff --git a/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py b/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py new file mode 100644 index 0000000000..813e6293f9 --- /dev/null +++ b/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .chat_completion_allowed_tools_param import ChatCompletionAllowedToolsParam + +__all__ = ["ChatCompletionAllowedToolChoiceParam"] + + +class ChatCompletionAllowedToolChoiceParam(TypedDict, total=False): + allowed_tools: Required[ChatCompletionAllowedToolsParam] + """Constrains the tools available to the model to a pre-defined set.""" + + type: Required[Literal["allowed_tools"]] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/src/openai/types/chat/chat_completion_allowed_tools_param.py b/src/openai/types/chat/chat_completion_allowed_tools_param.py new file mode 100644 index 0000000000..d9b72d8f34 --- /dev/null +++ b/src/openai/types/chat/chat_completion_allowed_tools_param.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionAllowedToolsParam"] + + +class ChatCompletionAllowedToolsParam(TypedDict, total=False): + mode: Required[Literal["auto", "required"]] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: Required[Iterable[Dict[str, object]]] + """A list of tool definitions that the model should be allowed to call. + + For the Chat Completions API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "function": { "name": "get_weather" } }, + { "type": "function", "function": { "name": "get_time" } } + ] + ``` + """ diff --git a/src/openai/types/chat/chat_completion_custom_tool_param.py b/src/openai/types/chat/chat_completion_custom_tool_param.py new file mode 100644 index 0000000000..14959ee449 --- /dev/null +++ b/src/openai/types/chat/chat_completion_custom_tool_param.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "ChatCompletionCustomToolParam", + "Custom", + "CustomFormat", + "CustomFormatText", + "CustomFormatGrammar", + "CustomFormatGrammarGrammar", +] + + +class CustomFormatText(TypedDict, total=False): + type: Required[Literal["text"]] + """Unconstrained text format. Always `text`.""" + + +class CustomFormatGrammarGrammar(TypedDict, total=False): + definition: Required[str] + """The grammar definition.""" + + syntax: Required[Literal["lark", "regex"]] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + +class CustomFormatGrammar(TypedDict, total=False): + grammar: Required[CustomFormatGrammarGrammar] + """Your chosen grammar.""" + + type: Required[Literal["grammar"]] + """Grammar format. Always `grammar`.""" + + +CustomFormat: TypeAlias = Union[CustomFormatText, CustomFormatGrammar] + + +class Custom(TypedDict, total=False): + name: Required[str] + """The name of the custom tool, used to identify it in tool calls.""" + + description: str + """Optional description of the custom tool, used to provide more context.""" + + format: CustomFormat + """The input format for the custom tool. Default is unconstrained text.""" + + +class ChatCompletionCustomToolParam(TypedDict, total=False): + custom: Required[Custom] + """Properties of the custom tool.""" + + type: Required[Literal["custom"]] + """The type of the custom tool. Always `custom`.""" diff --git a/src/openai/types/chat/chat_completion_tool.py b/src/openai/types/chat/chat_completion_function_tool.py similarity index 80% rename from src/openai/types/chat/chat_completion_tool.py rename to src/openai/types/chat/chat_completion_function_tool.py index ae9126f906..641568acf1 100644 --- a/src/openai/types/chat/chat_completion_tool.py +++ b/src/openai/types/chat/chat_completion_function_tool.py @@ -5,10 +5,10 @@ from ..._models import BaseModel from ..shared.function_definition import FunctionDefinition -__all__ = ["ChatCompletionTool"] +__all__ = ["ChatCompletionFunctionTool"] -class ChatCompletionTool(BaseModel): +class ChatCompletionFunctionTool(BaseModel): function: FunctionDefinition type: Literal["function"] diff --git a/src/openai/types/chat/chat_completion_function_tool_param.py b/src/openai/types/chat/chat_completion_function_tool_param.py new file mode 100644 index 0000000000..a39feea542 --- /dev/null +++ b/src/openai/types/chat/chat_completion_function_tool_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ..shared_params.function_definition import FunctionDefinition + +__all__ = ["ChatCompletionFunctionToolParam"] + + +class ChatCompletionFunctionToolParam(TypedDict, total=False): + function: Required[FunctionDefinition] + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_message_custom_tool_call.py b/src/openai/types/chat/chat_completion_message_custom_tool_call.py new file mode 100644 index 0000000000..b13c176afe --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_custom_tool_call.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionMessageCustomToolCall", "Custom"] + + +class Custom(BaseModel): + input: str + """The input for the custom tool call generated by the model.""" + + name: str + """The name of the custom tool to call.""" + + +class ChatCompletionMessageCustomToolCall(BaseModel): + id: str + """The ID of the tool call.""" + + custom: Custom + """The custom tool that the model called.""" + + type: Literal["custom"] + """The type of the tool. Always `custom`.""" diff --git a/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py b/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py new file mode 100644 index 0000000000..3753e0f200 --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionMessageCustomToolCallParam", "Custom"] + + +class Custom(TypedDict, total=False): + input: Required[str] + """The input for the custom tool call generated by the model.""" + + name: Required[str] + """The name of the custom tool to call.""" + + +class ChatCompletionMessageCustomToolCallParam(TypedDict, total=False): + id: Required[str] + """The ID of the tool call.""" + + custom: Required[Custom] + """The custom tool that the model called.""" + + type: Required[Literal["custom"]] + """The type of the tool. Always `custom`.""" diff --git a/src/openai/types/chat/chat_completion_message_function_tool_call.py b/src/openai/types/chat/chat_completion_message_function_tool_call.py new file mode 100644 index 0000000000..d056d9aff6 --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_function_tool_call.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ChatCompletionMessageFunctionToolCall", "Function"] + + +class Function(BaseModel): + arguments: str + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: str + """The name of the function to call.""" + + +class ChatCompletionMessageFunctionToolCall(BaseModel): + id: str + """The ID of the tool call.""" + + function: Function + """The function that the model called.""" + + type: Literal["function"] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_message_function_tool_call_param.py b/src/openai/types/chat/chat_completion_message_function_tool_call_param.py new file mode 100644 index 0000000000..7c827edd2c --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_function_tool_call_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionMessageFunctionToolCallParam", "Function"] + + +class Function(TypedDict, total=False): + arguments: Required[str] + """ + The arguments to call the function with, as generated by the model in JSON + format. Note that the model does not always generate valid JSON, and may + hallucinate parameters not defined by your function schema. Validate the + arguments in your code before calling your function. + """ + + name: Required[str] + """The name of the function to call.""" + + +class ChatCompletionMessageFunctionToolCallParam(TypedDict, total=False): + id: Required[str] + """The ID of the tool call.""" + + function: Required[Function] + """The function that the model called.""" + + type: Required[Literal["function"]] + """The type of the tool. Currently, only `function` is supported.""" diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py index 4fec667096..c254774626 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -1,31 +1,15 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing import Union +from typing_extensions import Annotated, TypeAlias -from ..._models import BaseModel +from ..._utils import PropertyInfo +from .chat_completion_message_custom_tool_call import ChatCompletionMessageCustomToolCall +from .chat_completion_message_function_tool_call import ChatCompletionMessageFunctionToolCall -__all__ = ["ChatCompletionMessageToolCall", "Function"] +__all__ = ["ChatCompletionMessageToolCall"] - -class Function(BaseModel): - arguments: str - """ - The arguments to call the function with, as generated by the model in JSON - format. Note that the model does not always generate valid JSON, and may - hallucinate parameters not defined by your function schema. Validate the - arguments in your code before calling your function. - """ - - name: str - """The name of the function to call.""" - - -class ChatCompletionMessageToolCall(BaseModel): - id: str - """The ID of the tool call.""" - - function: Function - """The function that the model called.""" - - type: Literal["function"] - """The type of the tool. Currently, only `function` is supported.""" +ChatCompletionMessageToolCall: TypeAlias = Annotated[ + Union[ChatCompletionMessageFunctionToolCall, ChatCompletionMessageCustomToolCall], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/chat/chat_completion_message_tool_call_param.py b/src/openai/types/chat/chat_completion_message_tool_call_param.py index f616c363d0..96ba6521f0 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call_param.py +++ b/src/openai/types/chat/chat_completion_message_tool_call_param.py @@ -2,30 +2,14 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union +from typing_extensions import TypeAlias -__all__ = ["ChatCompletionMessageToolCallParam", "Function"] +from .chat_completion_message_custom_tool_call_param import ChatCompletionMessageCustomToolCallParam +from .chat_completion_message_function_tool_call_param import ChatCompletionMessageFunctionToolCallParam +__all__ = ["ChatCompletionMessageToolCallParam"] -class Function(TypedDict, total=False): - arguments: Required[str] - """ - The arguments to call the function with, as generated by the model in JSON - format. Note that the model does not always generate valid JSON, and may - hallucinate parameters not defined by your function schema. Validate the - arguments in your code before calling your function. - """ - - name: Required[str] - """The name of the function to call.""" - - -class ChatCompletionMessageToolCallParam(TypedDict, total=False): - id: Required[str] - """The ID of the tool call.""" - - function: Required[Function] - """The function that the model called.""" - - type: Required[Literal["function"]] - """The type of the tool. Currently, only `function` is supported.""" +ChatCompletionMessageToolCallParam: TypeAlias = Union[ + ChatCompletionMessageFunctionToolCallParam, ChatCompletionMessageCustomToolCallParam +] diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py new file mode 100644 index 0000000000..1c123c0acb --- /dev/null +++ b/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatCompletionNamedToolChoiceCustomParam", "Custom"] + + +class Custom(TypedDict, total=False): + name: Required[str] + """The name of the custom tool to call.""" + + +class ChatCompletionNamedToolChoiceCustomParam(TypedDict, total=False): + custom: Required[Custom] + + type: Required[Literal["custom"]] + """For custom tool calling, the type is always `custom`.""" diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_param.py index 369f8b42dd..ae1acfb909 100644 --- a/src/openai/types/chat/chat_completion_named_tool_choice_param.py +++ b/src/openai/types/chat/chat_completion_named_tool_choice_param.py @@ -16,4 +16,4 @@ class ChatCompletionNamedToolChoiceParam(TypedDict, total=False): function: Required[Function] type: Required[Literal["function"]] - """The type of the tool. Currently, only `function` is supported.""" + """For function calling, the type is always `function`.""" diff --git a/src/openai/types/chat/chat_completion_stream_options_param.py b/src/openai/types/chat/chat_completion_stream_options_param.py index 471e0eba98..fc3191d2d1 100644 --- a/src/openai/types/chat/chat_completion_stream_options_param.py +++ b/src/openai/types/chat/chat_completion_stream_options_param.py @@ -8,6 +8,17 @@ class ChatCompletionStreamOptionsParam(TypedDict, total=False): + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + include_usage: bool """If set, an additional chunk will be streamed before the `data: [DONE]` message. diff --git a/src/openai/types/chat/chat_completion_tool_choice_option_param.py b/src/openai/types/chat/chat_completion_tool_choice_option_param.py index 7dedf041b7..f3bb0a46df 100644 --- a/src/openai/types/chat/chat_completion_tool_choice_option_param.py +++ b/src/openai/types/chat/chat_completion_tool_choice_option_param.py @@ -6,9 +6,14 @@ from typing_extensions import Literal, TypeAlias from .chat_completion_named_tool_choice_param import ChatCompletionNamedToolChoiceParam +from .chat_completion_allowed_tool_choice_param import ChatCompletionAllowedToolChoiceParam +from .chat_completion_named_tool_choice_custom_param import ChatCompletionNamedToolChoiceCustomParam __all__ = ["ChatCompletionToolChoiceOptionParam"] ChatCompletionToolChoiceOptionParam: TypeAlias = Union[ - Literal["none", "auto", "required"], ChatCompletionNamedToolChoiceParam + Literal["none", "auto", "required"], + ChatCompletionAllowedToolChoiceParam, + ChatCompletionNamedToolChoiceParam, + ChatCompletionNamedToolChoiceCustomParam, ] diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py index 6c2b1a36f0..7cd9743ea3 100644 --- a/src/openai/types/chat/chat_completion_tool_param.py +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -2,15 +2,12 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union +from typing_extensions import TypeAlias -from ..shared_params.function_definition import FunctionDefinition +from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam +from .chat_completion_function_tool_param import ChatCompletionFunctionToolParam __all__ = ["ChatCompletionToolParam"] - -class ChatCompletionToolParam(TypedDict, total=False): - function: Required[FunctionDefinition] - - type: Required[Literal["function"]] - """The type of the tool. Currently, only `function` is supported.""" +ChatCompletionToolParam: TypeAlias = Union[ChatCompletionFunctionToolParam, ChatCompletionCustomToolParam] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 20d7c187f8..011067af1a 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -185,12 +185,12 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ reasoning_effort: Optional[ReasoningEffort] - """**o-series models only** - + """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ response_format: ResponseFormat @@ -287,9 +287,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): tools: Iterable[ChatCompletionToolParam] """A list of tools the model may call. - Currently, only functions are supported as a tool. Use this to provide a list of - functions the model may generate JSON inputs for. A max of 128 functions are - supported. + You can provide either + [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + or [function tools](https://platform.openai.com/docs/guides/function-calling). """ top_logprobs: Optional[int] @@ -317,6 +317,14 @@ class CompletionCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ + web_search_options: WebSearchOptions """ This tool searches the web for relevant results to use in a response. Learn more diff --git a/src/openai/types/chat/parsed_function_tool_call.py b/src/openai/types/chat/parsed_function_tool_call.py index 3e90789f85..e06b3546cb 100644 --- a/src/openai/types/chat/parsed_function_tool_call.py +++ b/src/openai/types/chat/parsed_function_tool_call.py @@ -2,7 +2,7 @@ from typing import Optional -from .chat_completion_message_tool_call import Function, ChatCompletionMessageToolCall +from .chat_completion_message_function_tool_call import Function, ChatCompletionMessageFunctionToolCall __all__ = ["ParsedFunctionToolCall", "ParsedFunction"] @@ -24,6 +24,6 @@ class ParsedFunction(Function): """ -class ParsedFunctionToolCall(ChatCompletionMessageToolCall): +class ParsedFunctionToolCall(ChatCompletionMessageFunctionToolCall): function: ParsedFunction """The function that the model called.""" diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index a0eaa5addb..bb39d1d3e5 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -6,10 +6,10 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from ..shared.metadata import Metadata -from ..chat.chat_completion_tool import ChatCompletionTool from ..shared.response_format_text import ResponseFormatText from ..responses.easy_input_message import EasyInputMessage from ..responses.response_input_text import ResponseInputText +from ..chat.chat_completion_function_tool import ChatCompletionFunctionTool from ..shared.response_format_json_object import ResponseFormatJSONObject from ..shared.response_format_json_schema import ResponseFormatJSONSchema @@ -186,7 +186,7 @@ class SamplingParams(BaseModel): temperature: Optional[float] = None """A higher temperature increases randomness in the outputs.""" - tools: Optional[List[ChatCompletionTool]] = None + tools: Optional[List[ChatCompletionFunctionTool]] = None """A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 8892b68b17..7c71ecbe88 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -6,10 +6,10 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..shared_params.metadata import Metadata -from ..chat.chat_completion_tool_param import ChatCompletionToolParam from ..responses.easy_input_message_param import EasyInputMessageParam from ..shared_params.response_format_text import ResponseFormatText from ..responses.response_input_text_param import ResponseInputTextParam +from ..chat.chat_completion_function_tool_param import ChatCompletionFunctionToolParam from ..shared_params.response_format_json_object import ResponseFormatJSONObject from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema @@ -180,7 +180,7 @@ class SamplingParams(TypedDict, total=False): temperature: float """A higher temperature increases randomness in the outputs.""" - tools: Iterable[ChatCompletionToolParam] + tools: Iterable[ChatCompletionFunctionToolParam] """A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 2e502ed69f..74d8688081 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -5,6 +5,7 @@ from .tool import Tool as Tool from .response import Response as Response from .tool_param import ToolParam as ToolParam +from .custom_tool import CustomTool as CustomTool from .computer_tool import ComputerTool as ComputerTool from .function_tool import FunctionTool as FunctionTool from .response_item import ResponseItem as ResponseItem @@ -23,15 +24,18 @@ from .tool_choice_mcp import ToolChoiceMcp as ToolChoiceMcp from .web_search_tool import WebSearchTool as WebSearchTool from .file_search_tool import FileSearchTool as FileSearchTool +from .custom_tool_param import CustomToolParam as CustomToolParam from .tool_choice_types import ToolChoiceTypes as ToolChoiceTypes from .easy_input_message import EasyInputMessage as EasyInputMessage from .response_item_list import ResponseItemList as ResponseItemList +from .tool_choice_custom import ToolChoiceCustom as ToolChoiceCustom from .computer_tool_param import ComputerToolParam as ComputerToolParam from .function_tool_param import FunctionToolParam as FunctionToolParam from .response_includable import ResponseIncludable as ResponseIncludable from .response_input_file import ResponseInputFile as ResponseInputFile from .response_input_item import ResponseInputItem as ResponseInputItem from .response_input_text import ResponseInputText as ResponseInputText +from .tool_choice_allowed import ToolChoiceAllowed as ToolChoiceAllowed from .tool_choice_options import ToolChoiceOptions as ToolChoiceOptions from .response_error_event import ResponseErrorEvent as ResponseErrorEvent from .response_input_image import ResponseInputImage as ResponseInputImage @@ -59,12 +63,15 @@ from .response_completed_event import ResponseCompletedEvent as ResponseCompletedEvent from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .tool_choice_custom_param import ToolChoiceCustomParam as ToolChoiceCustomParam from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent +from .response_custom_tool_call import ResponseCustomToolCall as ResponseCustomToolCall from .response_incomplete_event import ResponseIncompleteEvent as ResponseIncompleteEvent from .response_input_file_param import ResponseInputFileParam as ResponseInputFileParam from .response_input_item_param import ResponseInputItemParam as ResponseInputItemParam from .response_input_text_param import ResponseInputTextParam as ResponseInputTextParam from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .tool_choice_allowed_param import ToolChoiceAllowedParam as ToolChoiceAllowedParam from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam @@ -84,8 +91,10 @@ from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent +from .response_custom_tool_call_param import ResponseCustomToolCallParam as ResponseCustomToolCallParam from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_custom_tool_call_output import ResponseCustomToolCallOutput as ResponseCustomToolCallOutput from .response_function_tool_call_item import ResponseFunctionToolCallItem as ResponseFunctionToolCallItem from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam @@ -105,6 +114,9 @@ from .response_audio_transcript_delta_event import ( ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, ) +from .response_custom_tool_call_output_param import ( + ResponseCustomToolCallOutputParam as ResponseCustomToolCallOutputParam, +) from .response_mcp_call_arguments_done_event import ( ResponseMcpCallArgumentsDoneEvent as ResponseMcpCallArgumentsDoneEvent, ) @@ -153,6 +165,9 @@ from .response_mcp_list_tools_in_progress_event import ( ResponseMcpListToolsInProgressEvent as ResponseMcpListToolsInProgressEvent, ) +from .response_custom_tool_call_input_done_event import ( + ResponseCustomToolCallInputDoneEvent as ResponseCustomToolCallInputDoneEvent, +) from .response_reasoning_summary_part_done_event import ( ResponseReasoningSummaryPartDoneEvent as ResponseReasoningSummaryPartDoneEvent, ) @@ -162,6 +177,9 @@ from .response_web_search_call_in_progress_event import ( ResponseWebSearchCallInProgressEvent as ResponseWebSearchCallInProgressEvent, ) +from .response_custom_tool_call_input_delta_event import ( + ResponseCustomToolCallInputDeltaEvent as ResponseCustomToolCallInputDeltaEvent, +) from .response_file_search_call_in_progress_event import ( ResponseFileSearchCallInProgressEvent as ResponseFileSearchCallInProgressEvent, ) diff --git a/src/openai/types/responses/custom_tool.py b/src/openai/types/responses/custom_tool.py new file mode 100644 index 0000000000..c16ae715eb --- /dev/null +++ b/src/openai/types/responses/custom_tool.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.custom_tool_input_format import CustomToolInputFormat + +__all__ = ["CustomTool"] + + +class CustomTool(BaseModel): + name: str + """The name of the custom tool, used to identify it in tool calls.""" + + type: Literal["custom"] + """The type of the custom tool. Always `custom`.""" + + description: Optional[str] = None + """Optional description of the custom tool, used to provide more context.""" + + format: Optional[CustomToolInputFormat] = None + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/src/openai/types/responses/custom_tool_param.py b/src/openai/types/responses/custom_tool_param.py new file mode 100644 index 0000000000..2afc8b19b8 --- /dev/null +++ b/src/openai/types/responses/custom_tool_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ..shared_params.custom_tool_input_format import CustomToolInputFormat + +__all__ = ["CustomToolParam"] + + +class CustomToolParam(TypedDict, total=False): + name: Required[str] + """The name of the custom tool, used to identify it in tool calls.""" + + type: Required[Literal["custom"]] + """The type of the custom tool. Always `custom`.""" + + description: str + """Optional description of the custom tool, used to provide more context.""" + + format: CustomToolInputFormat + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index e59e86d2b7..1d9db361dd 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -19,6 +19,7 @@ from .response_output_message import ResponseOutputMessage from .response_output_refusal import ResponseOutputRefusal from .response_reasoning_item import ResponseReasoningItem +from .response_custom_tool_call import ResponseCustomToolCall from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch @@ -73,6 +74,7 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): LocalShellCallAction, McpListTools, ResponseCodeInterpreterToolCall, + ResponseCustomToolCall, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 7db466dfe7..07a82cb4ac 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -13,7 +13,9 @@ from ..shared.metadata import Metadata from ..shared.reasoning import Reasoning from .tool_choice_types import ToolChoiceTypes +from .tool_choice_custom import ToolChoiceCustom from .response_input_item import ResponseInputItem +from .tool_choice_allowed import ToolChoiceAllowed from .tool_choice_options import ToolChoiceOptions from .response_output_item import ResponseOutputItem from .response_text_config import ResponseTextConfig @@ -28,7 +30,9 @@ class IncompleteDetails(BaseModel): """The reason why the response is incomplete.""" -ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceTypes, ToolChoiceFunction, ToolChoiceMcp] +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, ToolChoiceAllowed, ToolChoiceTypes, ToolChoiceFunction, ToolChoiceMcp, ToolChoiceCustom +] class Response(BaseModel): @@ -116,8 +120,10 @@ class Response(BaseModel): Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. """ top_p: Optional[float] = None @@ -130,8 +136,8 @@ class Response(BaseModel): """ background: Optional[bool] = None - """Whether to run the model response in the background. - + """ + Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). """ @@ -253,18 +259,3 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ - @property - def output_text(self) -> str: - """Convenience property that aggregates all `output_text` items from the `output` - list. - - If no `output_text` content blocks exist, then an empty string is returned. - """ - texts: List[str] = [] - for output in self.output: - if output.type == "message": - for content in output.content: - if content.type == "output_text": - texts.append(content.text) - - return "".join(texts) diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 4a78d7c028..53af325328 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -14,12 +14,15 @@ from ..shared_params.metadata import Metadata from .tool_choice_types_param import ToolChoiceTypesParam from ..shared_params.reasoning import Reasoning +from .tool_choice_custom_param import ToolChoiceCustomParam +from .tool_choice_allowed_param import ToolChoiceAllowedParam from .response_text_config_param import ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam from ..shared_params.responses_model import ResponsesModel __all__ = [ "ResponseCreateParamsBase", + "StreamOptions", "ToolChoice", "ResponseCreateParamsNonStreaming", "ResponseCreateParamsStreaming", @@ -28,8 +31,8 @@ class ResponseCreateParamsBase(TypedDict, total=False): background: Optional[bool] - """Whether to run the model response in the background. - + """ + Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). """ @@ -169,6 +172,9 @@ class ResponseCreateParamsBase(TypedDict, total=False): store: Optional[bool] """Whether to store the generated model response for later retrieval via API.""" + stream_options: Optional[StreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + temperature: Optional[float] """What sampling temperature to use, between 0 and 2. @@ -207,8 +213,10 @@ class ResponseCreateParamsBase(TypedDict, total=False): Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **Function calls (custom tools)**: Functions that are defined by you, enabling - the model to call your own code. Learn more about + the model to call your own code with strongly typed arguments and outputs. + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. """ top_logprobs: Optional[int] @@ -245,8 +253,36 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ + + +class StreamOptions(TypedDict, total=False): + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + -ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceTypesParam, ToolChoiceFunctionParam, ToolChoiceMcpParam] +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowedParam, + ToolChoiceTypesParam, + ToolChoiceFunctionParam, + ToolChoiceMcpParam, + ToolChoiceCustomParam, +] class ResponseCreateParamsNonStreaming(ResponseCreateParamsBase, total=False): diff --git a/src/openai/types/responses/response_custom_tool_call.py b/src/openai/types/responses/response_custom_tool_call.py new file mode 100644 index 0000000000..38c650e662 --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCustomToolCall"] + + +class ResponseCustomToolCall(BaseModel): + call_id: str + """An identifier used to map this custom tool call to a tool call output.""" + + input: str + """The input for the custom tool call generated by the model.""" + + name: str + """The name of the custom tool being called.""" + + type: Literal["custom_tool_call"] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call in the OpenAI platform.""" diff --git a/src/openai/types/responses/response_custom_tool_call_input_delta_event.py b/src/openai/types/responses/response_custom_tool_call_input_delta_event.py new file mode 100644 index 0000000000..6c33102d75 --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call_input_delta_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCustomToolCallInputDeltaEvent"] + + +class ResponseCustomToolCallInputDeltaEvent(BaseModel): + delta: str + """The incremental input data (delta) for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this delta applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.delta"] + """The event type identifier.""" diff --git a/src/openai/types/responses/response_custom_tool_call_input_done_event.py b/src/openai/types/responses/response_custom_tool_call_input_done_event.py new file mode 100644 index 0000000000..35a2fee22b --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call_input_done_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCustomToolCallInputDoneEvent"] + + +class ResponseCustomToolCallInputDoneEvent(BaseModel): + input: str + """The complete input data for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this event applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.done"] + """The event type identifier.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output.py b/src/openai/types/responses/response_custom_tool_call_output.py new file mode 100644 index 0000000000..a2b4cc3000 --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call_output.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCustomToolCallOutput"] + + +class ResponseCustomToolCallOutput(BaseModel): + call_id: str + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: str + """The output from the custom tool call generated by your code.""" + + type: Literal["custom_tool_call_output"] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call output in the OpenAI platform.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output_param.py b/src/openai/types/responses/response_custom_tool_call_output_param.py new file mode 100644 index 0000000000..d52c525467 --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call_output_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCustomToolCallOutputParam"] + + +class ResponseCustomToolCallOutputParam(TypedDict, total=False): + call_id: Required[str] + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: Required[str] + """The output from the custom tool call generated by your code.""" + + type: Required[Literal["custom_tool_call_output"]] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: str + """The unique ID of the custom tool call output in the OpenAI platform.""" diff --git a/src/openai/types/responses/response_custom_tool_call_param.py b/src/openai/types/responses/response_custom_tool_call_param.py new file mode 100644 index 0000000000..e15beac29f --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCustomToolCallParam"] + + +class ResponseCustomToolCallParam(TypedDict, total=False): + call_id: Required[str] + """An identifier used to map this custom tool call to a tool call output.""" + + input: Required[str] + """The input for the custom tool call generated by the model.""" + + name: Required[str] + """The name of the custom tool being called.""" + + type: Required[Literal["custom_tool_call"]] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: str + """The unique ID of the custom tool call in the OpenAI platform.""" diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index 5fbd7c274b..d2b454fd2c 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -8,10 +8,12 @@ from .easy_input_message import EasyInputMessage from .response_output_message import ResponseOutputMessage from .response_reasoning_item import ResponseReasoningItem +from .response_custom_tool_call import ResponseCustomToolCall from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_custom_tool_call_output import ResponseCustomToolCallOutput from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_input_message_content_list import ResponseInputMessageContentList from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot @@ -299,6 +301,8 @@ class ItemReference(BaseModel): McpApprovalRequest, McpApprovalResponse, McpCall, + ResponseCustomToolCallOutput, + ResponseCustomToolCall, ItemReference, ], PropertyInfo(discriminator="type"), diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 70cd9116a9..0d5dbda85c 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -8,10 +8,12 @@ from .easy_input_message_param import EasyInputMessageParam from .response_output_message_param import ResponseOutputMessageParam from .response_reasoning_item_param import ResponseReasoningItemParam +from .response_custom_tool_call_param import ResponseCustomToolCallParam from .response_computer_tool_call_param import ResponseComputerToolCallParam from .response_function_tool_call_param import ResponseFunctionToolCallParam from .response_function_web_search_param import ResponseFunctionWebSearchParam from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam +from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam @@ -298,5 +300,7 @@ class ItemReference(TypedDict, total=False): McpApprovalRequest, McpApprovalResponse, McpCall, + ResponseCustomToolCallOutputParam, + ResponseCustomToolCallParam, ItemReference, ] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 024998671f..6ff36a4238 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -8,10 +8,12 @@ from .easy_input_message_param import EasyInputMessageParam from .response_output_message_param import ResponseOutputMessageParam from .response_reasoning_item_param import ResponseReasoningItemParam +from .response_custom_tool_call_param import ResponseCustomToolCallParam from .response_computer_tool_call_param import ResponseComputerToolCallParam from .response_function_tool_call_param import ResponseFunctionToolCallParam from .response_function_web_search_param import ResponseFunctionWebSearchParam from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam +from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam @@ -299,6 +301,8 @@ class ItemReference(TypedDict, total=False): McpApprovalRequest, McpApprovalResponse, McpCall, + ResponseCustomToolCallOutputParam, + ResponseCustomToolCallParam, ItemReference, ] diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index 62f8f6fb3f..2d3ee7b64e 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -7,6 +7,7 @@ from ..._models import BaseModel from .response_output_message import ResponseOutputMessage from .response_reasoning_item import ResponseReasoningItem +from .response_custom_tool_call import ResponseCustomToolCall from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch @@ -161,6 +162,7 @@ class McpApprovalRequest(BaseModel): McpCall, McpListTools, McpApprovalRequest, + ResponseCustomToolCall, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response_retrieve_params.py b/src/openai/types/responses/response_retrieve_params.py index a092bd7fb8..4013db85ce 100644 --- a/src/openai/types/responses/response_retrieve_params.py +++ b/src/openai/types/responses/response_retrieve_params.py @@ -17,6 +17,17 @@ class ResponseRetrieveParamsBase(TypedDict, total=False): See the `include` parameter for Response creation above for more information. """ + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + starting_after: int """The sequence number of the event after which to start streaming.""" diff --git a/src/openai/types/responses/response_stream_event.py b/src/openai/types/responses/response_stream_event.py index d62cf8969b..c0a317cd9d 100644 --- a/src/openai/types/responses/response_stream_event.py +++ b/src/openai/types/responses/response_stream_event.py @@ -40,9 +40,11 @@ from .response_file_search_call_searching_event import ResponseFileSearchCallSearchingEvent from .response_image_gen_call_in_progress_event import ResponseImageGenCallInProgressEvent from .response_mcp_list_tools_in_progress_event import ResponseMcpListToolsInProgressEvent +from .response_custom_tool_call_input_done_event import ResponseCustomToolCallInputDoneEvent from .response_reasoning_summary_part_done_event import ResponseReasoningSummaryPartDoneEvent from .response_reasoning_summary_text_done_event import ResponseReasoningSummaryTextDoneEvent from .response_web_search_call_in_progress_event import ResponseWebSearchCallInProgressEvent +from .response_custom_tool_call_input_delta_event import ResponseCustomToolCallInputDeltaEvent from .response_file_search_call_in_progress_event import ResponseFileSearchCallInProgressEvent from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent from .response_image_gen_call_partial_image_event import ResponseImageGenCallPartialImageEvent @@ -111,6 +113,8 @@ ResponseMcpListToolsInProgressEvent, ResponseOutputTextAnnotationAddedEvent, ResponseQueuedEvent, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 4399871e29..455ba01666 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -5,6 +5,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel +from .custom_tool import CustomTool from .computer_tool import ComputerTool from .function_tool import FunctionTool from .web_search_tool import WebSearchTool @@ -177,6 +178,16 @@ class LocalShell(BaseModel): Tool: TypeAlias = Annotated[ - Union[FunctionTool, FileSearchTool, WebSearchTool, ComputerTool, Mcp, CodeInterpreter, ImageGeneration, LocalShell], + Union[ + FunctionTool, + FileSearchTool, + WebSearchTool, + ComputerTool, + Mcp, + CodeInterpreter, + ImageGeneration, + LocalShell, + CustomTool, + ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/tool_choice_allowed.py b/src/openai/types/responses/tool_choice_allowed.py new file mode 100644 index 0000000000..d7921dcb2a --- /dev/null +++ b/src/openai/types/responses/tool_choice_allowed.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceAllowed"] + + +class ToolChoiceAllowed(BaseModel): + mode: Literal["auto", "required"] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: List[Dict[str, object]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Literal["allowed_tools"] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/src/openai/types/responses/tool_choice_allowed_param.py b/src/openai/types/responses/tool_choice_allowed_param.py new file mode 100644 index 0000000000..0712cab43b --- /dev/null +++ b/src/openai/types/responses/tool_choice_allowed_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceAllowedParam"] + + +class ToolChoiceAllowedParam(TypedDict, total=False): + mode: Required[Literal["auto", "required"]] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: Required[Iterable[Dict[str, object]]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Required[Literal["allowed_tools"]] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/src/openai/types/responses/tool_choice_custom.py b/src/openai/types/responses/tool_choice_custom.py new file mode 100644 index 0000000000..d600e53616 --- /dev/null +++ b/src/openai/types/responses/tool_choice_custom.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceCustom"] + + +class ToolChoiceCustom(BaseModel): + name: str + """The name of the custom tool to call.""" + + type: Literal["custom"] + """For custom tool calling, the type is always `custom`.""" diff --git a/src/openai/types/responses/tool_choice_custom_param.py b/src/openai/types/responses/tool_choice_custom_param.py new file mode 100644 index 0000000000..55bc53b730 --- /dev/null +++ b/src/openai/types/responses/tool_choice_custom_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceCustomParam"] + + +class ToolChoiceCustomParam(TypedDict, total=False): + name: Required[str] + """The name of the custom tool to call.""" + + type: Required[Literal["custom"]] + """For custom tool calling, the type is always `custom`.""" diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index a977f06e3f..ef9ec2ae36 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -5,6 +5,7 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from .custom_tool_param import CustomToolParam from .computer_tool_param import ComputerToolParam from .function_tool_param import FunctionToolParam from .web_search_tool_param import WebSearchToolParam @@ -186,6 +187,7 @@ class LocalShell(TypedDict, total=False): CodeInterpreter, ImageGeneration, LocalShell, + CustomToolParam, ] diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index 6ad0ed5e01..2930b9ae3b 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -12,5 +12,8 @@ from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters from .response_format_text import ResponseFormatText as ResponseFormatText +from .custom_tool_input_format import CustomToolInputFormat as CustomToolInputFormat from .response_format_json_object import ResponseFormatJSONObject as ResponseFormatJSONObject from .response_format_json_schema import ResponseFormatJSONSchema as ResponseFormatJSONSchema +from .response_format_text_python import ResponseFormatTextPython as ResponseFormatTextPython +from .response_format_text_grammar import ResponseFormatTextGrammar as ResponseFormatTextGrammar diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index 309368a384..727c60c1c0 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -5,6 +5,13 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", diff --git a/src/openai/types/shared/custom_tool_input_format.py b/src/openai/types/shared/custom_tool_input_format.py new file mode 100644 index 0000000000..53c8323ed2 --- /dev/null +++ b/src/openai/types/shared/custom_tool_input_format.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["CustomToolInputFormat", "Text", "Grammar"] + + +class Text(BaseModel): + type: Literal["text"] + """Unconstrained text format. Always `text`.""" + + +class Grammar(BaseModel): + definition: str + """The grammar definition.""" + + syntax: Literal["lark", "regex"] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Literal["grammar"] + """Grammar format. Always `grammar`.""" + + +CustomToolInputFormat: TypeAlias = Annotated[Union[Text, Grammar], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 107aab2e4a..24ce301526 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -11,12 +11,12 @@ class Reasoning(BaseModel): effort: Optional[ReasoningEffort] = None - """**o-series models only** - + """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None diff --git a/src/openai/types/shared/reasoning_effort.py b/src/openai/types/shared/reasoning_effort.py index ace21b67e4..4b960cd7e6 100644 --- a/src/openai/types/shared/reasoning_effort.py +++ b/src/openai/types/shared/reasoning_effort.py @@ -5,4 +5,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["low", "medium", "high"]] +ReasoningEffort: TypeAlias = Optional[Literal["minimal", "low", "medium", "high"]] diff --git a/src/openai/types/shared/response_format_text_grammar.py b/src/openai/types/shared/response_format_text_grammar.py new file mode 100644 index 0000000000..b02f99c1b8 --- /dev/null +++ b/src/openai/types/shared/response_format_text_grammar.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatTextGrammar"] + + +class ResponseFormatTextGrammar(BaseModel): + grammar: str + """The custom grammar for the model to follow.""" + + type: Literal["grammar"] + """The type of response format being defined. Always `grammar`.""" diff --git a/src/openai/types/shared/response_format_text_python.py b/src/openai/types/shared/response_format_text_python.py new file mode 100644 index 0000000000..4cd18d46fa --- /dev/null +++ b/src/openai/types/shared/response_format_text_python.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFormatTextPython"] + + +class ResponseFormatTextPython(BaseModel): + type: Literal["python"] + """The type of response format being defined. Always `python`.""" diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index 8894710807..b6c0912b0f 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -10,5 +10,6 @@ from .function_definition import FunctionDefinition as FunctionDefinition from .function_parameters import FunctionParameters as FunctionParameters from .response_format_text import ResponseFormatText as ResponseFormatText +from .custom_tool_input_format import CustomToolInputFormat as CustomToolInputFormat from .response_format_json_object import ResponseFormatJSONObject as ResponseFormatJSONObject from .response_format_json_schema import ResponseFormatJSONSchema as ResponseFormatJSONSchema diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index 6cd8e7f91f..a1e5ab9f30 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -7,6 +7,13 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", "gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", diff --git a/src/openai/types/shared_params/custom_tool_input_format.py b/src/openai/types/shared_params/custom_tool_input_format.py new file mode 100644 index 0000000000..37df393e39 --- /dev/null +++ b/src/openai/types/shared_params/custom_tool_input_format.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["CustomToolInputFormat", "Text", "Grammar"] + + +class Text(TypedDict, total=False): + type: Required[Literal["text"]] + """Unconstrained text format. Always `text`.""" + + +class Grammar(TypedDict, total=False): + definition: Required[str] + """The grammar definition.""" + + syntax: Required[Literal["lark", "regex"]] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Required[Literal["grammar"]] + """Grammar format. Always `grammar`.""" + + +CustomToolInputFormat: TypeAlias = Union[Text, Grammar] diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 73e1a008df..7eab2c76f7 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -12,12 +12,12 @@ class Reasoning(TypedDict, total=False): effort: Optional[ReasoningEffort] - """**o-series models only** - + """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `low`, `medium`, and `high`. Reducing reasoning effort can - result in faster responses and fewer tokens used on reasoning in a response. + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] diff --git a/src/openai/types/shared_params/reasoning_effort.py b/src/openai/types/shared_params/reasoning_effort.py index 6052c5ae15..4c095a28d7 100644 --- a/src/openai/types/shared_params/reasoning_effort.py +++ b/src/openai/types/shared_params/reasoning_effort.py @@ -7,4 +7,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["low", "medium", "high"]] +ReasoningEffort: TypeAlias = Optional[Literal["minimal", "low", "medium", "high"]] diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 8aeb654e38..875e024a51 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -36,7 +36,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: instructions="instructions", metadata={"foo": "string"}, name="name", - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", temperature=1, tool_resources={ @@ -135,7 +135,7 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="string", name="name", - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", temperature=1, tool_resources={ @@ -272,7 +272,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> instructions="instructions", metadata={"foo": "string"}, name="name", - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", temperature=1, tool_resources={ @@ -371,7 +371,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> metadata={"foo": "string"}, model="string", name="name", - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", temperature=1, tool_resources={ diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 86a296627e..440486bac5 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -59,7 +59,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", stream=False, temperature=1, @@ -150,7 +150,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", temperature=1, tool_choice="none", @@ -609,7 +609,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", stream=False, temperature=1, @@ -700,7 +700,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="low", + reasoning_effort="minimal", response_format="auto", temperature=1, tool_choice="none", diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 2758d980ed..358ea18cbb 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -73,7 +73,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="low", + reasoning_effort="minimal", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, @@ -81,7 +81,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: stop="\n", store=True, stream=False, - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, temperature=1, tool_choice="none", tools=[ @@ -98,6 +101,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: top_logprobs=0, top_p=1, user="user-1234", + verbosity="low", web_search_options={ "search_context_size": "low", "user_location": { @@ -202,14 +206,17 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="low", + reasoning_effort="minimal", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, service_tier="auto", stop="\n", store=True, - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, temperature=1, tool_choice="none", tools=[ @@ -226,6 +233,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: top_logprobs=0, top_p=1, user="user-1234", + verbosity="low", web_search_options={ "search_context_size": "low", "user_location": { @@ -506,7 +514,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="low", + reasoning_effort="minimal", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, @@ -514,7 +522,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn stop="\n", store=True, stream=False, - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, temperature=1, tool_choice="none", tools=[ @@ -531,6 +542,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn top_logprobs=0, top_p=1, user="user-1234", + verbosity="low", web_search_options={ "search_context_size": "low", "user_location": { @@ -635,14 +647,17 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="low", + reasoning_effort="minimal", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, service_tier="auto", stop="\n", store=True, - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, temperature=1, tool_choice="none", tools=[ @@ -659,6 +674,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn top_logprobs=0, top_p=1, user="user-1234", + verbosity="low", web_search_options={ "search_context_size": "low", "user_location": { diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index 1c5271df75..a8fb0e59eb 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -41,7 +41,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: seed=0, stop="\n", stream=False, - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, suffix="test.", temperature=1, top_p=1, @@ -100,7 +103,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: presence_penalty=-2, seed=0, stop="\n", - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, suffix="test.", temperature=1, top_p=1, @@ -165,7 +171,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn seed=0, stop="\n", stream=False, - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, suffix="test.", temperature=1, top_p=1, @@ -224,7 +233,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn presence_penalty=-2, seed=0, stop="\n", - stream_options={"include_usage": True}, + stream_options={ + "include_obfuscation": True, + "include_usage": True, + }, suffix="test.", temperature=1, top_p=1, diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 63e47d8a69..4f8c88fa27 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -45,7 +45,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, prompt_cache_key="prompt-cache-key-1234", reasoning={ - "effort": "low", + "effort": "minimal", "generate_summary": "auto", "summary": "auto", }, @@ -53,6 +53,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: service_tier="auto", store=True, stream=False, + stream_options={"include_obfuscation": True}, temperature=1, text={"format": {"type": "text"}}, tool_choice="none", @@ -69,6 +70,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: top_p=1, truncation="auto", user="user-1234", + verbosity="low", ) assert_matches_type(Response, response, path=["response"]) @@ -120,13 +122,14 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, prompt_cache_key="prompt-cache-key-1234", reasoning={ - "effort": "low", + "effort": "minimal", "generate_summary": "auto", "summary": "auto", }, safety_identifier="safety-identifier-1234", service_tier="auto", store=True, + stream_options={"include_obfuscation": True}, temperature=1, text={"format": {"type": "text"}}, tool_choice="none", @@ -143,6 +146,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: top_p=1, truncation="auto", user="user-1234", + verbosity="low", ) response_stream.response.close() @@ -181,6 +185,7 @@ def test_method_retrieve_with_all_params_overload_1(self, client: OpenAI) -> Non response = client.responses.retrieve( response_id="resp_677efb5139a88190b512bc3fef8e535d", include=["code_interpreter_call.outputs"], + include_obfuscation=True, starting_after=0, stream=False, ) @@ -231,6 +236,7 @@ def test_method_retrieve_with_all_params_overload_2(self, client: OpenAI) -> Non response_id="resp_677efb5139a88190b512bc3fef8e535d", stream=True, include=["code_interpreter_call.outputs"], + include_obfuscation=True, starting_after=0, ) response_stream.response.close() @@ -386,7 +392,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, prompt_cache_key="prompt-cache-key-1234", reasoning={ - "effort": "low", + "effort": "minimal", "generate_summary": "auto", "summary": "auto", }, @@ -394,6 +400,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn service_tier="auto", store=True, stream=False, + stream_options={"include_obfuscation": True}, temperature=1, text={"format": {"type": "text"}}, tool_choice="none", @@ -410,6 +417,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn top_p=1, truncation="auto", user="user-1234", + verbosity="low", ) assert_matches_type(Response, response, path=["response"]) @@ -461,13 +469,14 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, prompt_cache_key="prompt-cache-key-1234", reasoning={ - "effort": "low", + "effort": "minimal", "generate_summary": "auto", "summary": "auto", }, safety_identifier="safety-identifier-1234", service_tier="auto", store=True, + stream_options={"include_obfuscation": True}, temperature=1, text={"format": {"type": "text"}}, tool_choice="none", @@ -484,6 +493,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn top_p=1, truncation="auto", user="user-1234", + verbosity="low", ) await response_stream.response.aclose() @@ -522,6 +532,7 @@ async def test_method_retrieve_with_all_params_overload_1(self, async_client: As response = await async_client.responses.retrieve( response_id="resp_677efb5139a88190b512bc3fef8e535d", include=["code_interpreter_call.outputs"], + include_obfuscation=True, starting_after=0, stream=False, ) @@ -572,6 +583,7 @@ async def test_method_retrieve_with_all_params_overload_2(self, async_client: As response_id="resp_677efb5139a88190b512bc3fef8e535d", stream=True, include=["code_interpreter_call.outputs"], + include_obfuscation=True, starting_after=0, ) await response_stream.response.aclose() From 657f551dbe583ffb259d987dafae12c6211fba06 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 7 Aug 2025 18:11:34 +0100 Subject: [PATCH 036/408] fix(types): correct tool types --- src/openai/lib/streaming/responses/_events.py | 4 ++++ src/openai/types/responses/tool_param.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/openai/lib/streaming/responses/_events.py b/src/openai/lib/streaming/responses/_events.py index de3342ec9d..bdc47b834a 100644 --- a/src/openai/lib/streaming/responses/_events.py +++ b/src/openai/lib/streaming/responses/_events.py @@ -39,9 +39,11 @@ ResponseMcpListToolsInProgressEvent, ResponseWebSearchCallCompletedEvent, ResponseWebSearchCallSearchingEvent, + ResponseCustomToolCallInputDoneEvent, ResponseFileSearchCallCompletedEvent, ResponseFileSearchCallSearchingEvent, ResponseWebSearchCallInProgressEvent, + ResponseCustomToolCallInputDeltaEvent, ResponseFileSearchCallInProgressEvent, ResponseImageGenCallPartialImageEvent, ResponseReasoningSummaryPartDoneEvent, @@ -139,6 +141,8 @@ class ResponseCompletedEvent(RawResponseCompletedEvent, GenericModel, Generic[Te ResponseQueuedEvent, ResponseReasoningTextDeltaEvent, ResponseReasoningTextDoneEvent, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index ef9ec2ae36..f91e758559 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -5,12 +5,12 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..chat import ChatCompletionFunctionToolParam from .custom_tool_param import CustomToolParam from .computer_tool_param import ComputerToolParam from .function_tool_param import FunctionToolParam from .web_search_tool_param import WebSearchToolParam from .file_search_tool_param import FileSearchToolParam -from ..chat.chat_completion_tool_param import ChatCompletionToolParam __all__ = [ "ToolParam", @@ -191,4 +191,4 @@ class LocalShell(TypedDict, total=False): ] -ParseableToolParam: TypeAlias = Union[ToolParam, ChatCompletionToolParam] +ParseableToolParam: TypeAlias = Union[ToolParam, ChatCompletionFunctionToolParam] From 445af1e3d07fcfe1d047ced2436318419b7c889c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 17:12:09 +0000 Subject: [PATCH 037/408] release: 1.99.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 41be9f1017..9472ef89a3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.1" + ".": "1.99.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4585135511..a6ac2ffb3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 1.99.2 (2025-08-07) + +Full Changelog: [v1.99.1...v1.99.2](https://github.com/openai/openai-python/compare/v1.99.1...v1.99.2) + +### Features + +* **api:** adds GPT-5 and new API features: platform.openai.com/docs/guides/gpt-5 ([ed370d8](https://github.com/openai/openai-python/commit/ed370d805e4d5d1ec14a136f5b2516751277059f)) + + +### Bug Fixes + +* **types:** correct tool types ([0c57bd7](https://github.com/openai/openai-python/commit/0c57bd7f2183a20b714d04edea380a4df0464a40)) + + +### Chores + +* **tests:** bump inline-snapshot dependency ([e236fde](https://github.com/openai/openai-python/commit/e236fde99a335fcaac9760f324e4807ce2cf7cba)) + ## 1.99.1 (2025-08-05) Full Changelog: [v1.99.0...v1.99.1](https://github.com/openai/openai-python/compare/v1.99.0...v1.99.1) diff --git a/pyproject.toml b/pyproject.toml index c71e8c135b..7ea0a63597 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.1" +version = "1.99.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 3fa80adba0..088935379f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.1" # x-release-please-version +__version__ = "1.99.2" # x-release-please-version From e3c0612c2cf39e7289fa3d91116c6eae83e534e6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 18:27:13 +0000 Subject: [PATCH 038/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 9c1b4e4c54..4d8b1f059e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f5c45f4ae5c2075cbc603d6910bba3da31c23714c209fbd3fd82a94f634a126b.yml openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba -config_hash: 9a64321968e21ed72f5c0e02164ea00d +config_hash: e53ea2d984c4e05a57eb0227fa379b2b From e574c12f9e2e738451ac010bdc52f4ee59813cfb Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Thu, 7 Aug 2025 20:22:50 +0100 Subject: [PATCH 039/408] fix(responses): add output_text back --- src/openai/types/responses/response.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 07a82cb4ac..5ebb18fda4 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -259,3 +259,17 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ + @property + def output_text(self) -> str: + """Convenience property that aggregates all `output_text` items from the `output` list. + + If no `output_text` content blocks exist, then an empty string is returned. + """ + texts: List[str] = [] + for output in self.output: + if output.type == "message": + for content in output.content: + if content.type == "output_text": + texts.append(content.text) + + return "".join(texts) From e4ec91e776d0155752ab004432dbcd1ad8a81d98 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 19:23:26 +0000 Subject: [PATCH 040/408] release: 1.99.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9472ef89a3..62255b70d8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.2" + ".": "1.99.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ac2ffb3f..6d06c6548e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.99.3 (2025-08-07) + +Full Changelog: [v1.99.2...v1.99.3](https://github.com/openai/openai-python/compare/v1.99.2...v1.99.3) + +### Bug Fixes + +* **responses:** add output_text back ([585a4f1](https://github.com/openai/openai-python/commit/585a4f15e5a088bf8afee745bc4a7803775ac283)) + ## 1.99.2 (2025-08-07) Full Changelog: [v1.99.1...v1.99.2](https://github.com/openai/openai-python/compare/v1.99.1...v1.99.2) diff --git a/pyproject.toml b/pyproject.toml index 7ea0a63597..b2fc253ae6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.2" +version = "1.99.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 088935379f..982cd9724f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.2" # x-release-please-version +__version__ = "1.99.3" # x-release-please-version From c81195ea2c8e7cded4d6e6fe66d0062efbf3d744 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 19:56:02 +0000 Subject: [PATCH 041/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4d8b1f059e..b82ecf95fa 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f5c45f4ae5c2075cbc603d6910bba3da31c23714c209fbd3fd82a94f634a126b.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d7e255da603b878e7e823135520211ce6a9e02890c9d549bbf3953a877ee5ef3.yml openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba -config_hash: e53ea2d984c4e05a57eb0227fa379b2b +config_hash: f0e0ce47bee61bd779ccaad22930f186 From 2ae42a399755828f74ced0f2fa41d9bd3a83a198 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 20:09:45 +0000 Subject: [PATCH 042/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index b82ecf95fa..a73b73fc2c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d7e255da603b878e7e823135520211ce6a9e02890c9d549bbf3953a877ee5ef3.yml openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba -config_hash: f0e0ce47bee61bd779ccaad22930f186 +config_hash: 2e7cf948f94e24f94c7d12ba2de2734a From 458a542a5f08dcf481292dfb04879cab27629b0c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:24:58 +0000 Subject: [PATCH 043/408] fix(types): rename chat completion tool --- .stats.yml | 4 +-- api.md | 2 +- src/openai/lib/_parsing/_completions.py | 18 ++++++------- src/openai/lib/streaming/chat/_completions.py | 15 +++++------ .../resources/chat/completions/completions.py | 26 +++++++++---------- src/openai/types/chat/__init__.py | 2 +- ...py => chat_completion_tool_union_param.py} | 4 +-- .../types/chat/completion_create_params.py | 4 +-- 8 files changed, 37 insertions(+), 38 deletions(-) rename src/openai/types/chat/{chat_completion_tool_param.py => chat_completion_tool_union_param.py} (69%) diff --git a/.stats.yml b/.stats.yml index a73b73fc2c..6a34d9da6e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d7e255da603b878e7e823135520211ce6a9e02890c9d549bbf3953a877ee5ef3.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-56d3a72a5caa187aebcf9de169a6a28a9dc3f70a79d7467a03a9e22595936066.yml openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba -config_hash: 2e7cf948f94e24f94c7d12ba2de2734a +config_hash: 7e18239879286d68a48ac5487a649aa6 diff --git a/api.md b/api.md index f05b3f61ee..f58c401311 100644 --- a/api.md +++ b/api.md @@ -79,7 +79,7 @@ from openai.types.chat import ( ChatCompletionStreamOptions, ChatCompletionSystemMessageParam, ChatCompletionTokenLogprob, - ChatCompletionTool, + ChatCompletionToolUnion, ChatCompletionToolChoiceOption, ChatCompletionToolMessageParam, ChatCompletionUserMessageParam, diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index e14c33864d..fc0bd05e4d 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -21,13 +21,13 @@ ChatCompletionMessage, ParsedFunctionToolCall, ParsedChatCompletionMessage, + ChatCompletionToolUnionParam, ChatCompletionFunctionToolParam, completion_create_params, ) from ..._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError from ...types.shared_params import FunctionDefinition from ...types.chat.completion_create_params import ResponseFormat as ResponseFormatParam -from ...types.chat.chat_completion_tool_param import ChatCompletionToolParam from ...types.chat.chat_completion_message_function_tool_call import Function ResponseFormatT = TypeVar( @@ -41,7 +41,7 @@ def is_strict_chat_completion_tool_param( - tool: ChatCompletionToolParam, + tool: ChatCompletionToolUnionParam, ) -> TypeGuard[ChatCompletionFunctionToolParam]: """Check if the given tool is a strict ChatCompletionFunctionToolParam.""" if not tool["type"] == "function": @@ -53,7 +53,7 @@ def is_strict_chat_completion_tool_param( def select_strict_chat_completion_tools( - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, ) -> Iterable[ChatCompletionFunctionToolParam] | NotGiven: """Select only the strict ChatCompletionFunctionToolParams from the given tools.""" if not is_given(tools): @@ -63,7 +63,7 @@ def select_strict_chat_completion_tools( def validate_input_tools( - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, ) -> Iterable[ChatCompletionFunctionToolParam] | NotGiven: if not is_given(tools): return NOT_GIVEN @@ -86,7 +86,7 @@ def validate_input_tools( def parse_chat_completion( *, response_format: type[ResponseFormatT] | completion_create_params.ResponseFormat | NotGiven, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, chat_completion: ChatCompletion | ParsedChatCompletion[object], ) -> ParsedChatCompletion[ResponseFormatT]: if is_given(input_tools): @@ -166,13 +166,13 @@ def parse_chat_completion( def get_input_tool_by_name( - *, input_tools: list[ChatCompletionToolParam], name: str + *, input_tools: list[ChatCompletionToolUnionParam], name: str ) -> ChatCompletionFunctionToolParam | None: return next((t for t in input_tools if t["type"] == "function" and t.get("function", {}).get("name") == name), None) def parse_function_tool_arguments( - *, input_tools: list[ChatCompletionToolParam], function: Function | ParsedFunction + *, input_tools: list[ChatCompletionToolUnionParam], function: Function | ParsedFunction ) -> object | None: input_tool = get_input_tool_by_name(input_tools=input_tools, name=function.name) if not input_tool: @@ -218,7 +218,7 @@ def solve_response_format_t( def has_parseable_input( *, response_format: type | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, ) -> bool: if has_rich_response_format(response_format): return True @@ -246,7 +246,7 @@ def is_response_format_param(response_format: object) -> TypeGuard[ResponseForma return is_dict(response_format) -def is_parseable_tool(input_tool: ChatCompletionToolParam) -> bool: +def is_parseable_tool(input_tool: ChatCompletionToolUnionParam) -> bool: if input_tool["type"] != "function": return False diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index 1dff628a20..52a6a550b2 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -37,12 +37,11 @@ parse_function_tool_arguments, ) from ...._streaming import Stream, AsyncStream -from ....types.chat import ChatCompletionChunk, ParsedChatCompletion +from ....types.chat import ChatCompletionChunk, ParsedChatCompletion, ChatCompletionToolUnionParam from ...._exceptions import LengthFinishReasonError, ContentFilterFinishReasonError from ....types.chat.chat_completion import ChoiceLogprobs from ....types.chat.chat_completion_chunk import Choice as ChoiceChunk from ....types.chat.completion_create_params import ResponseFormat as ResponseFormatParam -from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam class ChatCompletionStream(Generic[ResponseFormatT]): @@ -59,7 +58,7 @@ def __init__( *, raw_stream: Stream[ChatCompletionChunk], response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, ) -> None: self._raw_stream = raw_stream self._response = raw_stream.response @@ -140,7 +139,7 @@ def __init__( api_request: Callable[[], Stream[ChatCompletionChunk]], *, response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, ) -> None: self.__stream: ChatCompletionStream[ResponseFormatT] | None = None self.__api_request = api_request @@ -182,7 +181,7 @@ def __init__( *, raw_stream: AsyncStream[ChatCompletionChunk], response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, ) -> None: self._raw_stream = raw_stream self._response = raw_stream.response @@ -263,7 +262,7 @@ def __init__( api_request: Awaitable[AsyncStream[ChatCompletionChunk]], *, response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven, + input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, ) -> None: self.__stream: AsyncChatCompletionStream[ResponseFormatT] | None = None self.__api_request = api_request @@ -315,7 +314,7 @@ class ChatCompletionStreamState(Generic[ResponseFormatT]): def __init__( self, *, - input_tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven = NOT_GIVEN, ) -> None: self.__current_completion_snapshot: ParsedChatCompletionSnapshot | None = None @@ -585,7 +584,7 @@ def _build_events( class ChoiceEventState: - def __init__(self, *, input_tools: list[ChatCompletionToolParam]) -> None: + def __init__(self, *, input_tools: list[ChatCompletionToolUnionParam]) -> None: self._input_tools = input_tools self._content_done = False diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 65f91396bd..9404d85192 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -47,9 +47,9 @@ from ....types.chat.chat_completion_chunk import ChatCompletionChunk from ....types.chat.parsed_chat_completion import ParsedChatCompletion from ....types.chat.chat_completion_deleted import ChatCompletionDeleted -from ....types.chat.chat_completion_tool_param import ChatCompletionToolParam from ....types.chat.chat_completion_audio_param import ChatCompletionAudioParam from ....types.chat.chat_completion_message_param import ChatCompletionMessageParam +from ....types.chat.chat_completion_tool_union_param import ChatCompletionToolUnionParam from ....types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam from ....types.chat.chat_completion_prediction_content_param import ChatCompletionPredictionContentParam from ....types.chat.chat_completion_tool_choice_option_param import ChatCompletionToolChoiceOptionParam @@ -111,7 +111,7 @@ def parse( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -266,7 +266,7 @@ def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -555,7 +555,7 @@ def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -844,7 +844,7 @@ def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -1133,7 +1133,7 @@ def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -1408,7 +1408,7 @@ def stream( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -1550,7 +1550,7 @@ async def parse( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -1705,7 +1705,7 @@ async def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -1994,7 +1994,7 @@ async def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -2283,7 +2283,7 @@ async def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -2572,7 +2572,7 @@ async def create( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -2847,7 +2847,7 @@ def stream( stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolParam] | NotGiven = NOT_GIVEN, + tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index ce1cf4522a..1a814816cf 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -21,13 +21,13 @@ ParsedFunction as ParsedFunction, ParsedFunctionToolCall as ParsedFunctionToolCall, ) -from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam as ChatCompletionAudioParam from .chat_completion_function_tool import ChatCompletionFunctionTool as ChatCompletionFunctionTool from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam from .chat_completion_store_message import ChatCompletionStoreMessage as ChatCompletionStoreMessage from .chat_completion_token_logprob import ChatCompletionTokenLogprob as ChatCompletionTokenLogprob from .chat_completion_reasoning_effort import ChatCompletionReasoningEffort as ChatCompletionReasoningEffort +from .chat_completion_tool_union_param import ChatCompletionToolUnionParam as ChatCompletionToolUnionParam from .chat_completion_content_part_text import ChatCompletionContentPartText as ChatCompletionContentPartText from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam as ChatCompletionCustomToolParam from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_union_param.py similarity index 69% rename from src/openai/types/chat/chat_completion_tool_param.py rename to src/openai/types/chat/chat_completion_tool_union_param.py index 7cd9743ea3..0f8bf7b0e7 100644 --- a/src/openai/types/chat/chat_completion_tool_param.py +++ b/src/openai/types/chat/chat_completion_tool_union_param.py @@ -8,6 +8,6 @@ from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam from .chat_completion_function_tool_param import ChatCompletionFunctionToolParam -__all__ = ["ChatCompletionToolParam"] +__all__ = ["ChatCompletionToolUnionParam"] -ChatCompletionToolParam: TypeAlias = Union[ChatCompletionFunctionToolParam, ChatCompletionCustomToolParam] +ChatCompletionToolUnionParam: TypeAlias = Union[ChatCompletionFunctionToolParam, ChatCompletionCustomToolParam] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 011067af1a..a3bc90b0a2 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -8,9 +8,9 @@ from ..shared.chat_model import ChatModel from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort -from .chat_completion_tool_param import ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam from .chat_completion_message_param import ChatCompletionMessageParam +from .chat_completion_tool_union_param import ChatCompletionToolUnionParam from ..shared_params.function_parameters import FunctionParameters from ..shared_params.response_format_text import ResponseFormatText from .chat_completion_stream_options_param import ChatCompletionStreamOptionsParam @@ -284,7 +284,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): are present. """ - tools: Iterable[ChatCompletionToolParam] + tools: Iterable[ChatCompletionToolUnionParam] """A list of tools the model may call. You can provide either From 05a35a57b2fc39acd9132e9a7b9f25d4a59be698 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 8 Aug 2025 12:28:58 +0100 Subject: [PATCH 044/408] fix(types): revert ChatCompletionToolParam to a TypedDict --- src/openai/types/chat/__init__.py | 1 + src/openai/types/chat/chat_completion_tool_param.py | 11 +++++++++++ tests/compat/test_tool_param.py | 8 ++++++++ 3 files changed, 20 insertions(+) create mode 100644 src/openai/types/chat/chat_completion_tool_param.py create mode 100644 tests/compat/test_tool_param.py diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 1a814816cf..c9e77ff41c 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -21,6 +21,7 @@ ParsedFunction as ParsedFunction, ParsedFunctionToolCall as ParsedFunctionToolCall, ) +from .chat_completion_tool_param import ChatCompletionToolParam as ChatCompletionToolParam from .chat_completion_audio_param import ChatCompletionAudioParam as ChatCompletionAudioParam from .chat_completion_function_tool import ChatCompletionFunctionTool as ChatCompletionFunctionTool from .chat_completion_message_param import ChatCompletionMessageParam as ChatCompletionMessageParam diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py new file mode 100644 index 0000000000..ef3b6d07c6 --- /dev/null +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypeAlias + +from .chat_completion_function_tool_param import ChatCompletionFunctionToolParam + +__all__ = ["ChatCompletionToolParam"] + +ChatCompletionToolParam: TypeAlias = ChatCompletionFunctionToolParam diff --git a/tests/compat/test_tool_param.py b/tests/compat/test_tool_param.py new file mode 100644 index 0000000000..f2f84c6e94 --- /dev/null +++ b/tests/compat/test_tool_param.py @@ -0,0 +1,8 @@ +from openai.types.chat import ChatCompletionToolParam + + +def test_tool_param_can_be_instantiated() -> None: + assert ChatCompletionToolParam(type="function", function={"name": "test"}) == { + "function": {"name": "test"}, + "type": "function", + } From 09f98acf6bf7b66e98a4b6c3e37433ccdee0e20e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:32:31 +0000 Subject: [PATCH 045/408] release: 1.99.4 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 62255b70d8..cdb9c7d0d7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.3" + ".": "1.99.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d06c6548e..f8fdb7a268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.99.4 (2025-08-08) + +Full Changelog: [v1.99.3...v1.99.4](https://github.com/openai/openai-python/compare/v1.99.3...v1.99.4) + +### Bug Fixes + +* **types:** rename chat completion tool ([8d3bf88](https://github.com/openai/openai-python/commit/8d3bf88f5bc11cf30b8b050c24b2cc5a3807614f)) +* **types:** revert ChatCompletionToolParam to a TypedDict ([3f4ae72](https://github.com/openai/openai-python/commit/3f4ae725af53e631ddc128c1c6862ecf0b08e073)) + ## 1.99.3 (2025-08-07) Full Changelog: [v1.99.2...v1.99.3](https://github.com/openai/openai-python/compare/v1.99.2...v1.99.3) diff --git a/pyproject.toml b/pyproject.toml index b2fc253ae6..b041682135 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.3" +version = "1.99.4" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 982cd9724f..04f835f838 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.3" # x-release-please-version +__version__ = "1.99.4" # x-release-please-version From f4e41b87f7bf5597dadb0e42e11d33c093e89b5c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:56:34 +0000 Subject: [PATCH 046/408] fix(client): fix verbosity parameter location in Responses fixes error with unsupported `verbosity` parameter by correctly placing it inside the `text` parameter --- .stats.yml | 4 +-- src/openai/resources/responses/responses.py | 34 ------------------- .../types/responses/response_create_params.py | 8 ----- .../types/responses/response_text_config.py | 9 +++++ .../responses/response_text_config_param.py | 11 +++++- tests/api_resources/test_responses.py | 24 ++++++++----- 6 files changed, 37 insertions(+), 53 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6a34d9da6e..1c85ee4a0c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-56d3a72a5caa187aebcf9de169a6a28a9dc3f70a79d7467a03a9e22595936066.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6a1bfd4738fff02ef5becc3fdb2bf0cd6c026f2c924d4147a2a515474477dd9a.yml openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba -config_hash: 7e18239879286d68a48ac5487a649aa6 +config_hash: a67c5e195a59855fe8a5db0dc61a3e7f diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 5ba22418ed..8983daf278 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -102,7 +102,6 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -291,10 +290,6 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). - verbosity: Constrains the verbosity of the model's response. Lower values will result in - more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -335,7 +330,6 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -524,10 +518,6 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). - verbosity: Constrains the verbosity of the model's response. Lower values will result in - more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -568,7 +558,6 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -757,10 +746,6 @@ def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). - verbosity: Constrains the verbosity of the model's response. Lower values will result in - more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -800,7 +785,6 @@ def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -838,7 +822,6 @@ def create( "top_p": top_p, "truncation": truncation, "user": user, - "verbosity": verbosity, }, response_create_params.ResponseCreateParamsStreaming if stream @@ -1485,7 +1468,6 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1674,10 +1656,6 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). - verbosity: Constrains the verbosity of the model's response. Lower values will result in - more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1718,7 +1696,6 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1907,10 +1884,6 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). - verbosity: Constrains the verbosity of the model's response. Lower values will result in - more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1951,7 +1924,6 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2140,10 +2112,6 @@ async def create( similar requests and to help OpenAI detect and prevent abuse. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). - verbosity: Constrains the verbosity of the model's response. Lower values will result in - more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2183,7 +2151,6 @@ async def create( top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2221,7 +2188,6 @@ async def create( "top_p": top_p, "truncation": truncation, "user": user, - "verbosity": verbosity, }, response_create_params.ResponseCreateParamsStreaming if stream diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 53af325328..ea91fa1265 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -253,14 +253,6 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ - verbosity: Optional[Literal["low", "medium", "high"]] - """Constrains the verbosity of the model's response. - - Lower values will result in more concise responses, while higher values will - result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. - """ - class StreamOptions(TypedDict, total=False): include_obfuscation: bool diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py index a1894a9176..c53546da6d 100644 --- a/src/openai/types/responses/response_text_config.py +++ b/src/openai/types/responses/response_text_config.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal from ..._models import BaseModel from .response_format_text_config import ResponseFormatTextConfig @@ -24,3 +25,11 @@ class ResponseTextConfig(BaseModel): ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. """ + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py index aec064bf89..1229fce35b 100644 --- a/src/openai/types/responses/response_text_config_param.py +++ b/src/openai/types/responses/response_text_config_param.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing_extensions import TypedDict +from typing import Optional +from typing_extensions import Literal, TypedDict from .response_format_text_config_param import ResponseFormatTextConfigParam @@ -25,3 +26,11 @@ class ResponseTextConfigParam(TypedDict, total=False): ensures the message the model generates is valid JSON. Using `json_schema` is preferred for models that support it. """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 4f8c88fa27..310800b87e 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -55,7 +55,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: stream=False, stream_options={"include_obfuscation": True}, temperature=1, - text={"format": {"type": "text"}}, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, tool_choice="none", tools=[ { @@ -70,7 +73,6 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: top_p=1, truncation="auto", user="user-1234", - verbosity="low", ) assert_matches_type(Response, response, path=["response"]) @@ -131,7 +133,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: store=True, stream_options={"include_obfuscation": True}, temperature=1, - text={"format": {"type": "text"}}, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, tool_choice="none", tools=[ { @@ -146,7 +151,6 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: top_p=1, truncation="auto", user="user-1234", - verbosity="low", ) response_stream.response.close() @@ -402,7 +406,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn stream=False, stream_options={"include_obfuscation": True}, temperature=1, - text={"format": {"type": "text"}}, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, tool_choice="none", tools=[ { @@ -417,7 +424,6 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn top_p=1, truncation="auto", user="user-1234", - verbosity="low", ) assert_matches_type(Response, response, path=["response"]) @@ -478,7 +484,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn store=True, stream_options={"include_obfuscation": True}, temperature=1, - text={"format": {"type": "text"}}, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, tool_choice="none", tools=[ { @@ -493,7 +502,6 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn top_p=1, truncation="auto", user="user-1234", - verbosity="low", ) await response_stream.response.aclose() From 7aa3c787b99adf9b93f0652aacafa1200c681877 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:57:03 +0000 Subject: [PATCH 047/408] release: 1.99.5 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cdb9c7d0d7..393c24840d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.4" + ".": "1.99.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f8fdb7a268..3d332955ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.99.5 (2025-08-08) + +Full Changelog: [v1.99.4...v1.99.5](https://github.com/openai/openai-python/compare/v1.99.4...v1.99.5) + +### Bug Fixes + +* **client:** fix verbosity parameter location in Responses ([2764ff4](https://github.com/openai/openai-python/commit/2764ff459eb8b309d25b39b40e363b16a5b95019)) + ## 1.99.4 (2025-08-08) Full Changelog: [v1.99.3...v1.99.4](https://github.com/openai/openai-python/compare/v1.99.3...v1.99.4) diff --git a/pyproject.toml b/pyproject.toml index b041682135..ca255c95bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.4" +version = "1.99.5" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 04f835f838..12270a03d4 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.4" # x-release-please-version +__version__ = "1.99.5" # x-release-please-version From 52c48df8be298984eb2233fec71dc7765472f65e Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 8 Aug 2025 18:14:16 +0100 Subject: [PATCH 048/408] fix(types): re-export more tool call types --- src/openai/types/chat/chat_completion_message_tool_call.py | 4 ++-- src/openai/types/chat/chat_completion_tool_param.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py index c254774626..94cc086e9d 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -5,9 +5,9 @@ from ..._utils import PropertyInfo from .chat_completion_message_custom_tool_call import ChatCompletionMessageCustomToolCall -from .chat_completion_message_function_tool_call import ChatCompletionMessageFunctionToolCall +from .chat_completion_message_function_tool_call import Function as Function, ChatCompletionMessageFunctionToolCall -__all__ = ["ChatCompletionMessageToolCall"] +__all__ = ["ChatCompletionMessageToolCall", "Function"] ChatCompletionMessageToolCall: TypeAlias = Annotated[ Union[ChatCompletionMessageFunctionToolCall, ChatCompletionMessageCustomToolCall], diff --git a/src/openai/types/chat/chat_completion_tool_param.py b/src/openai/types/chat/chat_completion_tool_param.py index ef3b6d07c6..a18b13b471 100644 --- a/src/openai/types/chat/chat_completion_tool_param.py +++ b/src/openai/types/chat/chat_completion_tool_param.py @@ -4,8 +4,11 @@ from typing_extensions import TypeAlias -from .chat_completion_function_tool_param import ChatCompletionFunctionToolParam +from .chat_completion_function_tool_param import ( + FunctionDefinition as FunctionDefinition, + ChatCompletionFunctionToolParam, +) -__all__ = ["ChatCompletionToolParam"] +__all__ = ["ChatCompletionToolParam", "FunctionDefinition"] ChatCompletionToolParam: TypeAlias = ChatCompletionFunctionToolParam From 5dc3476754d02f487a7eefc743b97053ff4b533f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 17:58:48 +0000 Subject: [PATCH 049/408] chore: update @stainless-api/prism-cli to v5.15.0 --- scripts/mock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mock b/scripts/mock index d2814ae6a0..0b28f6ea23 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & # Wait for server to come online echo -n "Waiting for server" @@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" fi From 4df12615b6dd4bcc860d4064920878749195b80e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 21:23:11 +0000 Subject: [PATCH 050/408] chore(internal): update comment in script --- scripts/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test b/scripts/test index 2b87845670..dbeda2d217 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! prism_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the prism command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" echo exit 1 From 4d8c14cdc13772f6cc68be5eee6772b215f82c58 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 05:04:12 +0000 Subject: [PATCH 051/408] release: 1.99.6 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 393c24840d..03128d3ade 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.5" + ".": "1.99.6" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d332955ef..8edff34439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.99.6 (2025-08-09) + +Full Changelog: [v1.99.5...v1.99.6](https://github.com/openai/openai-python/compare/v1.99.5...v1.99.6) + +### Bug Fixes + +* **types:** re-export more tool call types ([8fe5741](https://github.com/openai/openai-python/commit/8fe574131cfe8f0453788cc6105d22834e7c102f)) + + +### Chores + +* **internal:** update comment in script ([e407bb5](https://github.com/openai/openai-python/commit/e407bb52112ad73e5eedf929434ee4ff7ac5a5a8)) +* update @stainless-api/prism-cli to v5.15.0 ([a1883fc](https://github.com/openai/openai-python/commit/a1883fcdfa02b81e5129bdb43206597a51f885fa)) + ## 1.99.5 (2025-08-08) Full Changelog: [v1.99.4...v1.99.5](https://github.com/openai/openai-python/compare/v1.99.4...v1.99.5) diff --git a/pyproject.toml b/pyproject.toml index ca255c95bd..37e9d4f767 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.5" +version = "1.99.6" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 12270a03d4..eed63aadba 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.5" # x-release-please-version +__version__ = "1.99.6" # x-release-please-version From bff85cddc49047d2e2a31c08ed1dfa2c8dcdd255 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 12:56:29 +0000 Subject: [PATCH 052/408] fix(types): rename ChatCompletionMessageToolCallParam --- .stats.yml | 4 ++-- api.md | 2 +- src/openai/types/chat/__init__.py | 8 ++++---- .../types/chat/chat_completion_assistant_message_param.py | 4 ++-- src/openai/types/chat/chat_completion_message.py | 4 ++-- .../types/chat/chat_completion_message_tool_call.py | 4 ++-- ...y => chat_completion_message_tool_call_union_param.py} | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) rename src/openai/types/chat/{chat_completion_message_tool_call_param.py => chat_completion_message_tool_call_union_param.py} (81%) diff --git a/.stats.yml b/.stats.yml index 1c85ee4a0c..a098c3d40d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6a1bfd4738fff02ef5becc3fdb2bf0cd6c026f2c924d4147a2a515474477dd9a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9cadfad609f94f20ebf74fdc06a80302f1a324dc69700a309a8056aabca82fd2.yml openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba -config_hash: a67c5e195a59855fe8a5db0dc61a3e7f +config_hash: 68337b532875626269c304372a669f67 diff --git a/api.md b/api.md index f58c401311..92b068b134 100644 --- a/api.md +++ b/api.md @@ -69,7 +69,7 @@ from openai.types.chat import ( ChatCompletionMessageCustomToolCall, ChatCompletionMessageFunctionToolCall, ChatCompletionMessageParam, - ChatCompletionMessageToolCall, + ChatCompletionMessageToolCallUnion, ChatCompletionModality, ChatCompletionNamedToolChoice, ChatCompletionNamedToolChoiceCustom, diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index c9e77ff41c..25ad0bfda6 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -31,7 +31,7 @@ from .chat_completion_tool_union_param import ChatCompletionToolUnionParam as ChatCompletionToolUnionParam from .chat_completion_content_part_text import ChatCompletionContentPartText as ChatCompletionContentPartText from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam as ChatCompletionCustomToolParam -from .chat_completion_message_tool_call import ChatCompletionMessageToolCall as ChatCompletionMessageToolCall +from .chat_completion_message_tool_call import ChatCompletionMessageToolCallUnion as ChatCompletionMessageToolCallUnion from .chat_completion_content_part_image import ChatCompletionContentPartImage as ChatCompletionContentPartImage from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam @@ -52,9 +52,6 @@ from .chat_completion_developer_message_param import ( ChatCompletionDeveloperMessageParam as ChatCompletionDeveloperMessageParam, ) -from .chat_completion_message_tool_call_param import ( - ChatCompletionMessageToolCallParam as ChatCompletionMessageToolCallParam, -) from .chat_completion_named_tool_choice_param import ( ChatCompletionNamedToolChoiceParam as ChatCompletionNamedToolChoiceParam, ) @@ -82,6 +79,9 @@ from .chat_completion_message_function_tool_call import ( ChatCompletionMessageFunctionToolCall as ChatCompletionMessageFunctionToolCall, ) +from .chat_completion_message_tool_call_union_param import ( + ChatCompletionMessageToolCallUnionParam as ChatCompletionMessageToolCallUnionParam, +) from .chat_completion_content_part_input_audio_param import ( ChatCompletionContentPartInputAudioParam as ChatCompletionContentPartInputAudioParam, ) diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 35e3a3d784..212d933e9b 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -6,8 +6,8 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam -from .chat_completion_message_tool_call_param import ChatCompletionMessageToolCallParam from .chat_completion_content_part_refusal_param import ChatCompletionContentPartRefusalParam +from .chat_completion_message_tool_call_union_param import ChatCompletionMessageToolCallUnionParam __all__ = ["ChatCompletionAssistantMessageParam", "Audio", "ContentArrayOfContentPart", "FunctionCall"] @@ -66,5 +66,5 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): refusal: Optional[str] """The refusal message by the assistant.""" - tool_calls: Iterable[ChatCompletionMessageToolCallParam] + tool_calls: Iterable[ChatCompletionMessageToolCallUnionParam] """The tool calls generated by the model, such as function calls.""" diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index c659ac3da0..5bb153fe3f 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -5,7 +5,7 @@ from ..._models import BaseModel from .chat_completion_audio import ChatCompletionAudio -from .chat_completion_message_tool_call import ChatCompletionMessageToolCall +from .chat_completion_message_tool_call import ChatCompletionMessageToolCallUnion __all__ = ["ChatCompletionMessage", "Annotation", "AnnotationURLCitation", "FunctionCall"] @@ -75,5 +75,5 @@ class ChatCompletionMessage(BaseModel): model. """ - tool_calls: Optional[List[ChatCompletionMessageToolCall]] = None + tool_calls: Optional[List[ChatCompletionMessageToolCallUnion]] = None """The tool calls generated by the model, such as function calls.""" diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py index 94cc086e9d..df687b19bd 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -7,9 +7,9 @@ from .chat_completion_message_custom_tool_call import ChatCompletionMessageCustomToolCall from .chat_completion_message_function_tool_call import Function as Function, ChatCompletionMessageFunctionToolCall -__all__ = ["ChatCompletionMessageToolCall", "Function"] +__all__ = [ "Function", "ChatCompletionMessageToolCallUnion"] -ChatCompletionMessageToolCall: TypeAlias = Annotated[ +ChatCompletionMessageToolCallUnion: TypeAlias = Annotated[ Union[ChatCompletionMessageFunctionToolCall, ChatCompletionMessageCustomToolCall], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/chat/chat_completion_message_tool_call_param.py b/src/openai/types/chat/chat_completion_message_tool_call_union_param.py similarity index 81% rename from src/openai/types/chat/chat_completion_message_tool_call_param.py rename to src/openai/types/chat/chat_completion_message_tool_call_union_param.py index 96ba6521f0..fcca9bb116 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call_param.py +++ b/src/openai/types/chat/chat_completion_message_tool_call_union_param.py @@ -8,8 +8,8 @@ from .chat_completion_message_custom_tool_call_param import ChatCompletionMessageCustomToolCallParam from .chat_completion_message_function_tool_call_param import ChatCompletionMessageFunctionToolCallParam -__all__ = ["ChatCompletionMessageToolCallParam"] +__all__ = ["ChatCompletionMessageToolCallUnionParam"] -ChatCompletionMessageToolCallParam: TypeAlias = Union[ +ChatCompletionMessageToolCallUnionParam: TypeAlias = Union[ ChatCompletionMessageFunctionToolCallParam, ChatCompletionMessageCustomToolCallParam ] From a6beda8e67a29c21d2fd2c447a9cb6c61fc1685c Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 11 Aug 2025 14:08:27 +0100 Subject: [PATCH 053/408] fix(types): revert ChatCompletionMessageToolCallParam to a TypedDict --- src/openai/types/chat/__init__.py | 3 +++ .../chat_completion_message_tool_call_param.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/openai/types/chat/chat_completion_message_tool_call_param.py diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 25ad0bfda6..2aecaf7d0c 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -52,6 +52,9 @@ from .chat_completion_developer_message_param import ( ChatCompletionDeveloperMessageParam as ChatCompletionDeveloperMessageParam, ) +from .chat_completion_message_tool_call_param import ( + ChatCompletionMessageToolCallParam as ChatCompletionMessageToolCallParam, +) from .chat_completion_named_tool_choice_param import ( ChatCompletionNamedToolChoiceParam as ChatCompletionNamedToolChoiceParam, ) diff --git a/src/openai/types/chat/chat_completion_message_tool_call_param.py b/src/openai/types/chat/chat_completion_message_tool_call_param.py new file mode 100644 index 0000000000..6baa1b57ab --- /dev/null +++ b/src/openai/types/chat/chat_completion_message_tool_call_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypeAlias + +from .chat_completion_message_function_tool_call_param import ( + Function as Function, + ChatCompletionMessageFunctionToolCallParam, +) + +__all__ = ["ChatCompletionMessageToolCallParam", "Function"] + +ChatCompletionMessageToolCallParam: TypeAlias = ChatCompletionMessageFunctionToolCallParam From 23887e4b9180f62e634f95ae4dff1ace447a630a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:09:54 +0000 Subject: [PATCH 054/408] release: 1.99.7 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 03128d3ade..804a6039aa 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.6" + ".": "1.99.7" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8edff34439..74d0da964a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.99.7 (2025-08-11) + +Full Changelog: [v1.99.6...v1.99.7](https://github.com/openai/openai-python/compare/v1.99.6...v1.99.7) + +### Bug Fixes + +* **types:** rename ChatCompletionMessageToolCallParam ([48085e2](https://github.com/openai/openai-python/commit/48085e2f473799d079e71d48d2f5612a6fbeb976)) +* **types:** revert ChatCompletionMessageToolCallParam to a TypedDict ([c8e9cec](https://github.com/openai/openai-python/commit/c8e9cec5c93cc022fff546f27161717f769d1f81)) + ## 1.99.6 (2025-08-09) Full Changelog: [v1.99.5...v1.99.6](https://github.com/openai/openai-python/compare/v1.99.5...v1.99.6) diff --git a/pyproject.toml b/pyproject.toml index 37e9d4f767..d58b9b1eb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.6" +version = "1.99.7" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index eed63aadba..3db3f866cf 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.6" # x-release-please-version +__version__ = "1.99.7" # x-release-please-version From f03096cb7ce9343fd88f16e1c1b93dcc794279b4 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 11 Aug 2025 16:19:50 +0100 Subject: [PATCH 055/408] chore(internal/tests): add inline snapshot format command --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index d58b9b1eb2..97ec8cf43d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -150,6 +150,9 @@ filterwarnings = [ "error" ] +[tool.inline-snapshot] +format-command="ruff format --stdin-filename {filename}" + [tool.pyright] # this enables practically every flag given by pyright. # there are a couple of flags that are still disabled by From 266edeba335834f2009e59c0a4a1ded8cb45749d Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 11 Aug 2025 16:20:48 +0100 Subject: [PATCH 056/408] refactor(tests): share snapshot utils --- tests/lib/chat/test_completions.py | 120 +++---------------- tests/lib/chat/test_completions_streaming.py | 2 +- tests/lib/snapshots.py | 99 +++++++++++++++ tests/lib/{chat/_utils.py => utils.py} | 2 +- 4 files changed, 118 insertions(+), 105 deletions(-) create mode 100644 tests/lib/snapshots.py rename tests/lib/{chat/_utils.py => utils.py} (98%) diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index d0bd14ce9e..3ef2e74c19 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -1,12 +1,9 @@ from __future__ import annotations -import os -import json from enum import Enum -from typing import Any, List, Callable, Optional, Awaitable +from typing import List, Optional from typing_extensions import Literal, TypeVar -import httpx import pytest from respx import MockRouter from pydantic import Field, BaseModel @@ -17,8 +14,9 @@ from openai._utils import assert_signatures_in_sync from openai._compat import PYDANTIC_V2 -from ._utils import print_obj, get_snapshot_value +from ..utils import print_obj from ...conftest import base_url +from ..snapshots import make_snapshot_request, make_async_snapshot_request from ..schema_types.query import Query _T = TypeVar("_T") @@ -32,7 +30,7 @@ @pytest.mark.respx(base_url=base_url) def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -100,7 +98,7 @@ class Location(BaseModel): temperature: float units: Literal["c", "f"] - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -170,7 +168,7 @@ class Location(BaseModel): temperature: float units: Optional[Literal["c", "f"]] = None - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -247,7 +245,7 @@ class ColorDetection(BaseModel): if not PYDANTIC_V2: ColorDetection.update_forward_refs(**locals()) # type: ignore - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -292,7 +290,7 @@ class Location(BaseModel): temperature: float units: Literal["c", "f"] - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -375,7 +373,7 @@ class CalendarEvent: date: str participants: List[str] - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -436,7 +434,7 @@ class CalendarEvent: @pytest.mark.respx(base_url=base_url) def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -521,7 +519,7 @@ class Location(BaseModel): units: Literal["c", "f"] with pytest.raises(openai.LengthFinishReasonError): - _make_snapshot_request( + make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -548,7 +546,7 @@ class Location(BaseModel): temperature: float units: Literal["c", "f"] - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -596,7 +594,7 @@ class GetWeatherArgs(BaseModel): country: str units: Literal["c", "f"] = "c" - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -662,7 +660,7 @@ class GetStockPrice(BaseModel): ticker: str exchange: str - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -733,7 +731,7 @@ class GetStockPrice(BaseModel): @pytest.mark.respx(base_url=base_url) def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: - completion = _make_snapshot_request( + completion = make_snapshot_request( lambda c: c.chat.completions.parse( model="gpt-4o-2024-08-06", messages=[ @@ -830,7 +828,7 @@ class Location(BaseModel): temperature: float units: Literal["c", "f"] - response = _make_snapshot_request( + response = make_snapshot_request( lambda c: c.chat.completions.with_raw_response.parse( model="gpt-4o-2024-08-06", messages=[ @@ -906,7 +904,7 @@ class Location(BaseModel): temperature: float units: Literal["c", "f"] - response = await _make_async_snapshot_request( + response = await make_async_snapshot_request( lambda c: c.chat.completions.with_raw_response.parse( model="gpt-4o-2024-08-06", messages=[ @@ -981,87 +979,3 @@ def test_parse_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpe checking_client.chat.completions.parse, exclude_params={"response_format", "stream"}, ) - - -def _make_snapshot_request( - func: Callable[[OpenAI], _T], - *, - content_snapshot: Any, - respx_mock: MockRouter, - mock_client: OpenAI, -) -> _T: - live = os.environ.get("OPENAI_LIVE") == "1" - if live: - - def _on_response(response: httpx.Response) -> None: - # update the content snapshot - assert json.dumps(json.loads(response.read())) == content_snapshot - - respx_mock.stop() - - client = OpenAI( - http_client=httpx.Client( - event_hooks={ - "response": [_on_response], - } - ) - ) - else: - respx_mock.post("/chat/completions").mock( - return_value=httpx.Response( - 200, - content=get_snapshot_value(content_snapshot), - headers={"content-type": "application/json"}, - ) - ) - - client = mock_client - - result = func(client) - - if live: - client.close() - - return result - - -async def _make_async_snapshot_request( - func: Callable[[AsyncOpenAI], Awaitable[_T]], - *, - content_snapshot: Any, - respx_mock: MockRouter, - mock_client: AsyncOpenAI, -) -> _T: - live = os.environ.get("OPENAI_LIVE") == "1" - if live: - - async def _on_response(response: httpx.Response) -> None: - # update the content snapshot - assert json.dumps(json.loads(await response.aread())) == content_snapshot - - respx_mock.stop() - - client = AsyncOpenAI( - http_client=httpx.AsyncClient( - event_hooks={ - "response": [_on_response], - } - ) - ) - else: - respx_mock.post("/chat/completions").mock( - return_value=httpx.Response( - 200, - content=get_snapshot_value(content_snapshot), - headers={"content-type": "application/json"}, - ) - ) - - client = mock_client - - result = await func(client) - - if live: - await client.close() - - return result diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 1daa98c6a0..65826d28d9 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -30,7 +30,7 @@ ) from openai.lib._parsing._completions import ResponseFormatT -from ._utils import print_obj, get_snapshot_value +from ..utils import print_obj, get_snapshot_value from ...conftest import base_url _T = TypeVar("_T") diff --git a/tests/lib/snapshots.py b/tests/lib/snapshots.py new file mode 100644 index 0000000000..64b1163338 --- /dev/null +++ b/tests/lib/snapshots.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +import os +import json +from typing import Any, Callable, Awaitable +from typing_extensions import TypeVar + +import httpx +from respx import MockRouter + +from openai import OpenAI, AsyncOpenAI + +from .utils import get_snapshot_value + +_T = TypeVar("_T") + + +def make_snapshot_request( + func: Callable[[OpenAI], _T], + *, + content_snapshot: Any, + respx_mock: MockRouter, + mock_client: OpenAI, +) -> _T: + live = os.environ.get("OPENAI_LIVE") == "1" + if live: + + def _on_response(response: httpx.Response) -> None: + # update the content snapshot + assert json.dumps(json.loads(response.read())) == content_snapshot + + respx_mock.stop() + + client = OpenAI( + http_client=httpx.Client( + event_hooks={ + "response": [_on_response], + } + ) + ) + else: + respx_mock.post("/chat/completions").mock( + return_value=httpx.Response( + 200, + content=get_snapshot_value(content_snapshot), + headers={"content-type": "application/json"}, + ) + ) + + client = mock_client + + result = func(client) + + if live: + client.close() + + return result + + +async def make_async_snapshot_request( + func: Callable[[AsyncOpenAI], Awaitable[_T]], + *, + content_snapshot: Any, + respx_mock: MockRouter, + mock_client: AsyncOpenAI, +) -> _T: + live = os.environ.get("OPENAI_LIVE") == "1" + if live: + + async def _on_response(response: httpx.Response) -> None: + # update the content snapshot + assert json.dumps(json.loads(await response.aread())) == content_snapshot + + respx_mock.stop() + + client = AsyncOpenAI( + http_client=httpx.AsyncClient( + event_hooks={ + "response": [_on_response], + } + ) + ) + else: + respx_mock.post("/chat/completions").mock( + return_value=httpx.Response( + 200, + content=get_snapshot_value(content_snapshot), + headers={"content-type": "application/json"}, + ) + ) + + client = mock_client + + result = await func(client) + + if live: + await client.close() + + return result diff --git a/tests/lib/chat/_utils.py b/tests/lib/utils.py similarity index 98% rename from tests/lib/chat/_utils.py rename to tests/lib/utils.py index 0cc1c99952..2129ee811a 100644 --- a/tests/lib/chat/_utils.py +++ b/tests/lib/utils.py @@ -7,7 +7,7 @@ import pytest import pydantic -from ...utils import rich_print_str +from ..utils import rich_print_str ReprArgs: TypeAlias = "Iterable[tuple[str | None, Any]]" From fd0af12000ff807e558039d9780e0e41bbf6bf2f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 11 Aug 2025 16:20:56 +0100 Subject: [PATCH 057/408] chore(internal): fix formatting --- src/openai/types/chat/chat_completion_message_tool_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py index df687b19bd..be01179701 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -7,7 +7,7 @@ from .chat_completion_message_custom_tool_call import ChatCompletionMessageCustomToolCall from .chat_completion_message_function_tool_call import Function as Function, ChatCompletionMessageFunctionToolCall -__all__ = [ "Function", "ChatCompletionMessageToolCallUnion"] +__all__ = ["Function", "ChatCompletionMessageToolCallUnion"] ChatCompletionMessageToolCallUnion: TypeAlias = Annotated[ Union[ChatCompletionMessageFunctionToolCall, ChatCompletionMessageCustomToolCall], From a4cd0b5086a419ccf02981f61dccb4b23f6e85a0 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 11 Aug 2025 16:28:37 +0100 Subject: [PATCH 058/408] chore(tests): add responses output_text test --- tests/lib/chat/test_completions.py | 14 ++++++++++ tests/lib/responses/__init__.py | 0 tests/lib/responses/test_responses.py | 40 +++++++++++++++++++++++++++ tests/lib/snapshots.py | 6 ++-- 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/lib/responses/__init__.py create mode 100644 tests/lib/responses/test_responses.py diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 3ef2e74c19..0371f6828b 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -43,6 +43,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte content_snapshot=snapshot( '{"id": "chatcmpl-ABfvaueLEMLNYbT8YzpJxsmiQ6HSY", "object": "chat.completion", "created": 1727346142, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "I\'m unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or app like the Weather Channel or a local news station.", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 14, "completion_tokens": 37, "total_tokens": 51, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -112,6 +113,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvbtVnTu5DeC4EFnRYj8mtfOM99", "object": "chat.completion", "created": 1727346143, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 14, "total_tokens": 93, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -182,6 +184,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvcC8grKYsRkSoMp9CCAhbXAd0b", "object": "chat.completion", "created": 1727346144, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 88, "completion_tokens": 14, "total_tokens": 102, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -256,6 +259,7 @@ class ColorDetection(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvjIatz0zrZu50gRbMtlp0asZpz", "object": "chat.completion", "created": 1727346151, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"color\\":\\"red\\",\\"hex_color_code\\":\\"#FF0000\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 109, "completion_tokens": 14, "total_tokens": 123, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -305,6 +309,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvp8qzboW92q8ONDF4DPHlI7ckC", "object": "chat.completion", "created": 1727346157, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":64,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 1, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}, {"index": 2, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":63.0,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 44, "total_tokens": 123, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -385,6 +390,7 @@ class CalendarEvent: content_snapshot=snapshot( '{"id": "chatcmpl-ABfvqhz4uUUWsw8Ohw2Mp9B4sKKV8", "object": "chat.completion", "created": 1727346158, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"name\\":\\"Science Fair\\",\\"date\\":\\"Friday\\",\\"participants\\":[\\"Alice\\",\\"Bob\\"]}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 92, "completion_tokens": 17, "total_tokens": 109, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_7568d46099"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -449,6 +455,7 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m content_snapshot=snapshot( '{"id": "chatcmpl-ABfvtNiaTNUF6OymZUnEFc9lPq9p1", "object": "chat.completion", "created": 1727346161, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_NKpApJybW1MzOjZO2FzwYw0d", "type": "function", "function": {"name": "Query", "arguments": "{\\"name\\":\\"May 2022 Fulfilled Orders Not Delivered on Time\\",\\"table_name\\":\\"orders\\",\\"columns\\":[\\"id\\",\\"status\\",\\"expected_delivery_date\\",\\"delivered_at\\",\\"shipped_at\\",\\"ordered_at\\",\\"canceled_at\\"],\\"conditions\\":[{\\"column\\":\\"ordered_at\\",\\"operator\\":\\">=\\",\\"value\\":\\"2022-05-01\\"},{\\"column\\":\\"ordered_at\\",\\"operator\\":\\"<=\\",\\"value\\":\\"2022-05-31\\"},{\\"column\\":\\"status\\",\\"operator\\":\\"=\\",\\"value\\":\\"fulfilled\\"},{\\"column\\":\\"delivered_at\\",\\"operator\\":\\">\\",\\"value\\":{\\"column_name\\":\\"expected_delivery_date\\"}}],\\"order_by\\":\\"asc\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 512, "completion_tokens": 132, "total_tokens": 644, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_7568d46099"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -534,6 +541,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvvX7eB1KsfeZj8VcF3z7G7SbaA", "object": "chat.completion", "created": 1727346163, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"", "refusal": null}, "logprobs": null, "finish_reason": "length"}], "usage": {"prompt_tokens": 79, "completion_tokens": 1, "total_tokens": 80, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_7568d46099"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -560,6 +568,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvwoKVWPQj2UPlAcAKM7s40GsRx", "object": "chat.completion", "created": 1727346164, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "refusal": "I\'m very sorry, but I can\'t assist with that."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 12, "total_tokens": 91, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -610,6 +619,7 @@ class GetWeatherArgs(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvx6Z4dchiW2nya1N8KMsHFrQRE", "object": "chat.completion", "created": 1727346165, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_Y6qJ7ofLgOrBnMD5WbVAeiRV", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\":\\"Edinburgh\\",\\"country\\":\\"UK\\",\\"units\\":\\"c\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 76, "completion_tokens": 24, "total_tokens": 100, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_e45dabd248"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -683,6 +693,7 @@ class GetStockPrice(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvyvfNWKcl7Ohqos4UFrmMs1v4C", "object": "chat.completion", "created": 1727346166, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_fdNz3vOBKYgOIpMdWotB9MjY", "type": "function", "function": {"name": "GetWeatherArgs", "arguments": "{\\"city\\": \\"Edinburgh\\", \\"country\\": \\"GB\\", \\"units\\": \\"c\\"}"}}, {"id": "call_h1DWI1POMJLb0KwIyQHWXD4p", "type": "function", "function": {"name": "get_stock_price", "arguments": "{\\"ticker\\": \\"AAPL\\", \\"exchange\\": \\"NASDAQ\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 149, "completion_tokens": 60, "total_tokens": 209, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_b40fb1c6fb"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -765,6 +776,7 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: content_snapshot=snapshot( '{"id": "chatcmpl-ABfvzdvCI6RaIkiEFNjqGXCSYnlzf", "object": "chat.completion", "created": 1727346167, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": null, "tool_calls": [{"id": "call_CUdUoJpsWWVdxXntucvnol1M", "type": "function", "function": {"name": "get_weather", "arguments": "{\\"city\\":\\"San Francisco\\",\\"state\\":\\"CA\\"}"}}], "refusal": null}, "logprobs": null, "finish_reason": "tool_calls"}], "usage": {"prompt_tokens": 48, "completion_tokens": 19, "total_tokens": 67, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -842,6 +854,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABrDYCa8W1w66eUxKDO8TQF1m6trT", "object": "chat.completion", "created": 1727389540, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":58,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 14, "total_tokens": 93, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) @@ -918,6 +931,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABrDQWOiw0PK5JOsxl1D9ooeQgznq", "object": "chat.completion", "created": 1727389532, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"city\\":\\"San Francisco\\",\\"temperature\\":65,\\"units\\":\\"f\\"}", "refusal": null}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 79, "completion_tokens": 14, "total_tokens": 93, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_5050236cbd"}' ), + path="/chat/completions", mock_client=async_client, respx_mock=respx_mock, ) diff --git a/tests/lib/responses/__init__.py b/tests/lib/responses/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py new file mode 100644 index 0000000000..d996127dcd --- /dev/null +++ b/tests/lib/responses/test_responses.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing_extensions import TypeVar + +import pytest +from respx import MockRouter +from inline_snapshot import snapshot + +from openai import OpenAI + +from ...conftest import base_url +from ..snapshots import make_snapshot_request + +_T = TypeVar("_T") + +# all the snapshots in this file are auto-generated from the live API +# +# you can update them with +# +# `OPENAI_LIVE=1 pytest --inline-snapshot=fix` + + +@pytest.mark.respx(base_url=base_url) +def test_output_text(client: OpenAI, respx_mock: MockRouter) -> None: + response = make_snapshot_request( + lambda c: c.responses.create( + model="gpt-4o-mini", + input="What's the weather like in SF?", + ), + content_snapshot=snapshot( + '{"id": "resp_689a0b2545288193953c892439b42e2800b2e36c65a1fd4b", "object": "response", "created_at": 1754925861, "status": "completed", "background": false, "error": null, "incomplete_details": null, "instructions": null, "max_output_tokens": null, "max_tool_calls": null, "model": "gpt-4o-mini-2024-07-18", "output": [{"id": "msg_689a0b2637b08193ac478e568f49e3f900b2e36c65a1fd4b", "type": "message", "status": "completed", "content": [{"type": "output_text", "annotations": [], "logprobs": [], "text": "I can\'t provide real-time updates, but you can easily check the current weather in San Francisco using a weather website or app. Typically, San Francisco has cool, foggy summers and mild winters, so it\'s good to be prepared for variable weather!"}], "role": "assistant"}], "parallel_tool_calls": true, "previous_response_id": null, "prompt_cache_key": null, "reasoning": {"effort": null, "summary": null}, "safety_identifier": null, "service_tier": "default", "store": true, "temperature": 1.0, "text": {"format": {"type": "text"}, "verbosity": "medium"}, "tool_choice": "auto", "tools": [], "top_logprobs": 0, "top_p": 1.0, "truncation": "disabled", "usage": {"input_tokens": 14, "input_tokens_details": {"cached_tokens": 0}, "output_tokens": 50, "output_tokens_details": {"reasoning_tokens": 0}, "total_tokens": 64}, "user": null, "metadata": {}}' + ), + path="/responses", + mock_client=client, + respx_mock=respx_mock, + ) + + assert response.output_text == snapshot( + "I can't provide real-time updates, but you can easily check the current weather in San Francisco using a weather website or app. Typically, San Francisco has cool, foggy summers and mild winters, so it's good to be prepared for variable weather!" + ) diff --git a/tests/lib/snapshots.py b/tests/lib/snapshots.py index 64b1163338..ed53edebcb 100644 --- a/tests/lib/snapshots.py +++ b/tests/lib/snapshots.py @@ -21,6 +21,7 @@ def make_snapshot_request( content_snapshot: Any, respx_mock: MockRouter, mock_client: OpenAI, + path: str, ) -> _T: live = os.environ.get("OPENAI_LIVE") == "1" if live: @@ -39,7 +40,7 @@ def _on_response(response: httpx.Response) -> None: ) ) else: - respx_mock.post("/chat/completions").mock( + respx_mock.post(path).mock( return_value=httpx.Response( 200, content=get_snapshot_value(content_snapshot), @@ -63,6 +64,7 @@ async def make_async_snapshot_request( content_snapshot: Any, respx_mock: MockRouter, mock_client: AsyncOpenAI, + path: str, ) -> _T: live = os.environ.get("OPENAI_LIVE") == "1" if live: @@ -81,7 +83,7 @@ async def _on_response(response: httpx.Response) -> None: ) ) else: - respx_mock.post("/chat/completions").mock( + respx_mock.post(path).mock( return_value=httpx.Response( 200, content=get_snapshot_value(content_snapshot), From 753d472ef8f14cda35bcd0a992813cb4af9ffef9 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 11 Aug 2025 16:30:26 +0100 Subject: [PATCH 059/408] fix(internal/tests): correct snapshot update comment --- tests/lib/chat/test_completions.py | 2 +- tests/lib/chat/test_completions_streaming.py | 2 +- tests/lib/responses/test_responses.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 0371f6828b..f04a0e3782 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -25,7 +25,7 @@ # # you can update them with # -# `OPENAI_LIVE=1 pytest --inline-snapshot=fix` +# `OPENAI_LIVE=1 pytest --inline-snapshot=fix -p no:xdist -o addopts=""` @pytest.mark.respx(base_url=base_url) diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 65826d28d9..fa17f67177 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -39,7 +39,7 @@ # # you can update them with # -# `OPENAI_LIVE=1 pytest --inline-snapshot=fix` +# `OPENAI_LIVE=1 pytest --inline-snapshot=fix -p no:xdist -o addopts=""` @pytest.mark.respx(base_url=base_url) diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py index d996127dcd..8ce3462e76 100644 --- a/tests/lib/responses/test_responses.py +++ b/tests/lib/responses/test_responses.py @@ -17,7 +17,7 @@ # # you can update them with # -# `OPENAI_LIVE=1 pytest --inline-snapshot=fix` +# `OPENAI_LIVE=1 pytest --inline-snapshot=fix -p no:xdist -o addopts=""` @pytest.mark.respx(base_url=base_url) From 37265a9d27e3596075d60499a0336698c11530d0 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 11 Aug 2025 21:13:29 +0100 Subject: [PATCH 060/408] fix(types): revert ChatCompletionMessageToolCallUnion breaking change --- src/openai/types/chat/__init__.py | 5 ++++- src/openai/types/chat/chat_completion_message_tool_call.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/openai/types/chat/__init__.py b/src/openai/types/chat/__init__.py index 2aecaf7d0c..50bdac7c65 100644 --- a/src/openai/types/chat/__init__.py +++ b/src/openai/types/chat/__init__.py @@ -31,7 +31,10 @@ from .chat_completion_tool_union_param import ChatCompletionToolUnionParam as ChatCompletionToolUnionParam from .chat_completion_content_part_text import ChatCompletionContentPartText as ChatCompletionContentPartText from .chat_completion_custom_tool_param import ChatCompletionCustomToolParam as ChatCompletionCustomToolParam -from .chat_completion_message_tool_call import ChatCompletionMessageToolCallUnion as ChatCompletionMessageToolCallUnion +from .chat_completion_message_tool_call import ( + ChatCompletionMessageToolCall as ChatCompletionMessageToolCall, + ChatCompletionMessageToolCallUnion as ChatCompletionMessageToolCallUnion, +) from .chat_completion_content_part_image import ChatCompletionContentPartImage as ChatCompletionContentPartImage from .chat_completion_content_part_param import ChatCompletionContentPartParam as ChatCompletionContentPartParam from .chat_completion_tool_message_param import ChatCompletionToolMessageParam as ChatCompletionToolMessageParam diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py index be01179701..845e639089 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -13,3 +13,5 @@ Union[ChatCompletionMessageFunctionToolCall, ChatCompletionMessageCustomToolCall], PropertyInfo(discriminator="type"), ] + +ChatCompletionMessageToolCall: TypeAlias = ChatCompletionMessageToolCallUnion From a02ac0dd5b4797d4a782b4b75fd0790df3e14149 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:14:05 +0000 Subject: [PATCH 061/408] release: 1.99.8 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 21 +++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 804a6039aa..5d9ceab581 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.7" + ".": "1.99.8" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 74d0da964a..33e0e8e948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 1.99.8 (2025-08-11) + +Full Changelog: [v1.99.7...v1.99.8](https://github.com/openai/openai-python/compare/v1.99.7...v1.99.8) + +### Bug Fixes + +* **internal/tests:** correct snapshot update comment ([2784a7a](https://github.com/openai/openai-python/commit/2784a7a7da24ddba74b5717f07d67546864472b9)) +* **types:** revert ChatCompletionMessageToolCallUnion breaking change ([ba54e03](https://github.com/openai/openai-python/commit/ba54e03bc2d21825d891685bf3bad4a9253cbeb0)) + + +### Chores + +* **internal/tests:** add inline snapshot format command ([8107db8](https://github.com/openai/openai-python/commit/8107db8ff738baa65fe4cf2f2d7f1acd29219c78)) +* **internal:** fix formatting ([f03a03d](https://github.com/openai/openai-python/commit/f03a03de8c84740209d021598ff8bf56b6d3c684)) +* **tests:** add responses output_text test ([971347b](https://github.com/openai/openai-python/commit/971347b3a05f79c51abd11c86b382ca73c28cefb)) + + +### Refactors + +* **tests:** share snapshot utils ([791c567](https://github.com/openai/openai-python/commit/791c567cd87fb8d587965773b1da0404c7848c68)) + ## 1.99.7 (2025-08-11) Full Changelog: [v1.99.6...v1.99.7](https://github.com/openai/openai-python/compare/v1.99.6...v1.99.7) diff --git a/pyproject.toml b/pyproject.toml index 97ec8cf43d..b4a7d01a2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.7" +version = "1.99.8" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 3db3f866cf..9d1f1f4e96 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.7" # x-release-please-version +__version__ = "1.99.8" # x-release-please-version From 064910b115e21837dd793390e6cfbeddd07e5f9a Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 12 Aug 2025 01:23:24 +0100 Subject: [PATCH 062/408] fix(types): actually fix ChatCompletionMessageToolCall type --- src/openai/types/chat/chat_completion_message_tool_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/types/chat/chat_completion_message_tool_call.py b/src/openai/types/chat/chat_completion_message_tool_call.py index 845e639089..71ac63f58e 100644 --- a/src/openai/types/chat/chat_completion_message_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_tool_call.py @@ -14,4 +14,4 @@ PropertyInfo(discriminator="type"), ] -ChatCompletionMessageToolCall: TypeAlias = ChatCompletionMessageToolCallUnion +ChatCompletionMessageToolCall: TypeAlias = ChatCompletionMessageFunctionToolCall From 34014aedbb8946c03e97e5c8d72e03ad2259cd7c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 00:24:03 +0000 Subject: [PATCH 063/408] release: 1.99.9 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5d9ceab581..2dfeb2d9bb 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.8" + ".": "1.99.9" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e0e8e948..392fb8b667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.99.9 (2025-08-12) + +Full Changelog: [v1.99.8...v1.99.9](https://github.com/openai/openai-python/compare/v1.99.8...v1.99.9) + +### Bug Fixes + +* **types:** actually fix ChatCompletionMessageToolCall type ([20cb0c8](https://github.com/openai/openai-python/commit/20cb0c86d598e196386ff43db992f6497eb756d0)) + ## 1.99.8 (2025-08-11) Full Changelog: [v1.99.7...v1.99.8](https://github.com/openai/openai-python/compare/v1.99.7...v1.99.8) diff --git a/pyproject.toml b/pyproject.toml index b4a7d01a2b..ced6079b6d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.8" +version = "1.99.9" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9d1f1f4e96..7d3b3da5d7 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.8" # x-release-please-version +__version__ = "1.99.9" # x-release-please-version From 0843a1116498bc3312db9904adf71a4fb0a0a77e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 19:11:41 +0000 Subject: [PATCH 064/408] feat(api): add new text parameters, expiration options --- .stats.yml | 6 +- src/openai/resources/batches.py | 10 ++ .../resources/beta/realtime/realtime.py | 8 +- .../resources/beta/realtime/sessions.py | 4 +- .../beta/realtime/transcription_sessions.py | 4 +- .../resources/beta/threads/runs/runs.py | 12 +- src/openai/resources/beta/threads/threads.py | 12 +- .../resources/chat/completions/completions.py | 48 +++++--- src/openai/resources/files.py | 14 ++- src/openai/resources/responses/responses.py | 107 ++++++------------ src/openai/resources/uploads/uploads.py | 10 ++ src/openai/types/batch_create_params.py | 23 +++- src/openai/types/beta/realtime/session.py | 2 +- .../beta/realtime/session_create_params.py | 2 +- .../beta/realtime/session_update_event.py | 2 +- .../realtime/session_update_event_param.py | 2 +- .../transcription_session_create_params.py | 2 +- .../realtime/transcription_session_update.py | 2 +- .../transcription_session_update_param.py | 2 +- .../beta/thread_create_and_run_params.py | 2 +- src/openai/types/beta/threads/run.py | 2 +- .../types/beta/threads/run_create_params.py | 2 +- src/openai/types/chat/chat_completion.py | 5 +- .../types/chat/chat_completion_chunk.py | 5 +- .../types/chat/completion_create_params.py | 18 ++- src/openai/types/file_create_params.py | 25 +++- src/openai/types/responses/__init__.py | 2 - src/openai/types/responses/response.py | 46 +++++--- .../types/responses/response_create_params.py | 45 +++++--- .../types/responses/response_text_config.py | 35 ------ .../responses/response_text_config_param.py | 36 ------ src/openai/types/upload_create_params.py | 25 +++- tests/api_resources/chat/test_completions.py | 4 + tests/api_resources/test_batches.py | 8 ++ tests/api_resources/test_files.py | 24 ++++ tests/api_resources/test_responses.py | 4 +- tests/api_resources/test_uploads.py | 28 +++++ 37 files changed, 343 insertions(+), 245 deletions(-) delete mode 100644 src/openai/types/responses/response_text_config.py delete mode 100644 src/openai/types/responses/response_text_config_param.py diff --git a/.stats.yml b/.stats.yml index a098c3d40d..66c46e7730 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9cadfad609f94f20ebf74fdc06a80302f1a324dc69700a309a8056aabca82fd2.yml -openapi_spec_hash: 3eb8d86c06f0bb5e1190983e5acfc9ba -config_hash: 68337b532875626269c304372a669f67 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-24be531010b354303d741fc9247c1f84f75978f9f7de68aca92cb4f240a04722.yml +openapi_spec_hash: 3e46f439f6a863beadc71577eb4efa15 +config_hash: ed87b9139ac595a04a2162d754df2fed diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 26ea498b31..2340bd2e32 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -49,6 +49,7 @@ def create( endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + output_expires_after: batch_create_params.OutputExpiresAfter | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -85,6 +86,9 @@ def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. + output_expires_after: The expiration policy for the output and/or error file that are generated for a + batch. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -101,6 +105,7 @@ def create( "endpoint": endpoint, "input_file_id": input_file_id, "metadata": metadata, + "output_expires_after": output_expires_after, }, batch_create_params.BatchCreateParams, ), @@ -259,6 +264,7 @@ async def create( endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + output_expires_after: batch_create_params.OutputExpiresAfter | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -295,6 +301,9 @@ async def create( Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. + output_expires_after: The expiration policy for the output and/or error file that are generated for a + batch. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -311,6 +320,7 @@ async def create( "endpoint": endpoint, "input_file_id": input_file_id, "metadata": metadata, + "output_expires_after": output_expires_after, }, batch_create_params.BatchCreateParams, ), diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 8e1b558cf3..7b99c7f6c4 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -652,8 +652,8 @@ def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | Not """Send this event to cancel an in-progress response. The server will respond - with a `response.cancelled` event or an error if there is no response to - cancel. + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. """ self._connection.send( cast( @@ -904,8 +904,8 @@ async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str """Send this event to cancel an in-progress response. The server will respond - with a `response.cancelled` event or an error if there is no response to - cancel. + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. """ await self._connection.send( cast( diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index e639c0ba43..eaddb384ce 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -152,7 +152,7 @@ def create( set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. Semantic VAD - is more advanced and uses a turn detection model (in conjuction with VAD) to + is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and wait longer @@ -334,7 +334,7 @@ async def create( set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. Semantic VAD - is more advanced and uses a turn detection model (in conjuction with VAD) to + is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and wait longer diff --git a/src/openai/resources/beta/realtime/transcription_sessions.py b/src/openai/resources/beta/realtime/transcription_sessions.py index 5f97b3c8e3..54fe7d5a6c 100644 --- a/src/openai/resources/beta/realtime/transcription_sessions.py +++ b/src/openai/resources/beta/realtime/transcription_sessions.py @@ -96,7 +96,7 @@ def create( set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. Semantic VAD - is more advanced and uses a turn detection model (in conjuction with VAD) to + is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and wait longer @@ -209,7 +209,7 @@ async def create( set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. Semantic VAD - is more advanced and uses a turn detection model (in conjuction with VAD) to + is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and wait longer diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 01246d7c12..07b43e6471 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -220,7 +220,7 @@ def create( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -370,7 +370,7 @@ def create( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -520,7 +520,7 @@ def create( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -1650,7 +1650,7 @@ async def create( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -1800,7 +1800,7 @@ async def create( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -1950,7 +1950,7 @@ async def create( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index ff2a41155d..dbe47d2d0e 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -393,7 +393,7 @@ def create_and_run( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -527,7 +527,7 @@ def create_and_run( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -661,7 +661,7 @@ def create_and_run( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -1251,7 +1251,7 @@ async def create_and_run( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -1385,7 +1385,7 @@ async def create_and_run( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers @@ -1519,7 +1519,7 @@ async def create_and_run( We generally recommend altering this or temperature but not both. truncation_strategy: Controls for how a thread will be truncated prior to the run. Use this to - control the intial context window of the run. + control the initial context window of the run. extra_headers: Send extra headers diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 9404d85192..bc5fe0fc05 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -103,6 +103,7 @@ def parse( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -203,6 +204,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), + "text": text, "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, @@ -265,6 +267,7 @@ def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -438,9 +441,8 @@ def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -554,6 +556,7 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -736,9 +739,8 @@ def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -843,6 +845,7 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1025,9 +1028,8 @@ def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -1132,6 +1134,7 @@ def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1178,6 +1181,7 @@ def create( "stream": stream, "stream_options": stream_options, "temperature": temperature, + "text": text, "tool_choice": tool_choice, "tools": tools, "top_logprobs": top_logprobs, @@ -1400,6 +1404,7 @@ def stream( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -1470,6 +1475,7 @@ def stream( presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, reasoning_effort=reasoning_effort, + text=text, safety_identifier=safety_identifier, seed=seed, service_tier=service_tier, @@ -1542,6 +1548,7 @@ async def parse( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -1642,6 +1649,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), + "text": text, "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, @@ -1704,6 +1712,7 @@ async def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1877,9 +1886,8 @@ async def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -1993,6 +2001,7 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2175,9 +2184,8 @@ async def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -2282,6 +2290,7 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2464,9 +2473,8 @@ async def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -2571,6 +2579,7 @@ async def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2617,6 +2626,7 @@ async def create( "stream": stream, "stream_options": stream_options, "temperature": temperature, + "text": text, "tool_choice": tool_choice, "tools": tools, "top_logprobs": top_logprobs, @@ -2839,6 +2849,7 @@ def stream( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, + text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -2910,6 +2921,7 @@ def stream( presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, reasoning_effort=reasoning_effort, + text=text, safety_identifier=safety_identifier, seed=seed, service_tier=service_tier, diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 179af870ba..b45b8f303f 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -57,6 +57,7 @@ def create( *, file: FileTypes, purpose: FilePurpose, + expires_after: file_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -68,7 +69,7 @@ def create( Individual files can be up to 512 MB, and the size of all files uploaded by one organization can be up - to 100 GB. + to 1 TB. The Assistants API supports files up to 2 million tokens and of specific file types. See the @@ -96,6 +97,9 @@ def create( fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: Flexible file type for any purpose - `evals`: Used for eval data sets + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -108,6 +112,7 @@ def create( { "file": file, "purpose": purpose, + "expires_after": expires_after, } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) @@ -369,6 +374,7 @@ async def create( *, file: FileTypes, purpose: FilePurpose, + expires_after: file_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -380,7 +386,7 @@ async def create( Individual files can be up to 512 MB, and the size of all files uploaded by one organization can be up - to 100 GB. + to 1 TB. The Assistants API supports files up to 2 million tokens and of specific file types. See the @@ -408,6 +414,9 @@ async def create( fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: Flexible file type for any purpose - `evals`: Used for eval data sets + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -420,6 +429,7 @@ async def create( { "file": file, "purpose": purpose, + "expires_after": expires_after, } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 8983daf278..97ad0faa94 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -43,7 +43,6 @@ from ...types.responses.response_input_param import ResponseInputParam from ...types.responses.response_prompt_param import ResponsePromptParam from ...types.responses.response_stream_event import ResponseStreamEvent -from ...types.responses.response_text_config_param import ResponseTextConfigParam __all__ = ["Responses", "AsyncResponses"] @@ -95,7 +94,7 @@ def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -195,7 +194,7 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning: **o-series models only** + reasoning: **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -214,9 +213,8 @@ def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -240,12 +238,6 @@ def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -323,7 +315,7 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -430,7 +422,7 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning: **o-series models only** + reasoning: **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -449,9 +441,8 @@ def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -468,12 +459,6 @@ def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -551,7 +536,7 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -658,7 +643,7 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning: **o-series models only** + reasoning: **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -677,9 +662,8 @@ def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -696,12 +680,6 @@ def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -778,7 +756,7 @@ def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -869,7 +847,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -901,7 +879,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -1030,7 +1008,7 @@ def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1461,7 +1439,7 @@ async def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1561,7 +1539,7 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning: **o-series models only** + reasoning: **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -1580,9 +1558,8 @@ async def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -1606,12 +1583,6 @@ async def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -1689,7 +1660,7 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1796,7 +1767,7 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning: **o-series models only** + reasoning: **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -1815,9 +1786,8 @@ async def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -1834,12 +1804,6 @@ async def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -1917,7 +1881,7 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2024,7 +1988,7 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - reasoning: **o-series models only** + reasoning: **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -2043,9 +2007,8 @@ async def create( - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -2062,12 +2025,6 @@ async def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. - text: Configuration options for a text response from the model. Can be plain text or - structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -2144,7 +2101,7 @@ async def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2235,7 +2192,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -2267,7 +2224,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -2400,7 +2357,7 @@ async def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: response_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index ecfcee4800..125a45e33c 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -170,6 +170,7 @@ def create( filename: str, mime_type: str, purpose: FilePurpose, + expires_after: upload_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -213,6 +214,9 @@ def create( See the [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose). + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -229,6 +233,7 @@ def create( "filename": filename, "mime_type": mime_type, "purpose": purpose, + "expires_after": expires_after, }, upload_create_params.UploadCreateParams, ), @@ -473,6 +478,7 @@ async def create( filename: str, mime_type: str, purpose: FilePurpose, + expires_after: upload_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -516,6 +522,9 @@ async def create( See the [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose). + expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire + after 30 days and all other files are persisted until they are manually deleted. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -532,6 +541,7 @@ async def create( "filename": filename, "mime_type": mime_type, "purpose": purpose, + "expires_after": expires_after, }, upload_create_params.UploadCreateParams, ), diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index cc95afd3ba..c0f9034d5e 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -7,7 +7,7 @@ from .shared_params.metadata import Metadata -__all__ = ["BatchCreateParams"] +__all__ = ["BatchCreateParams", "OutputExpiresAfter"] class BatchCreateParams(TypedDict, total=False): @@ -47,3 +47,24 @@ class BatchCreateParams(TypedDict, total=False): Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters. """ + + output_expires_after: OutputExpiresAfter + """ + The expiration policy for the output and/or error file that are generated for a + batch. + """ + + +class OutputExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["created_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `created_at`. Note that the anchor is the file creation time, + not the time the batch is created. + """ + + seconds: Required[int] + """The number of seconds after the anchor time that the file will expire. + + Must be between 3600 (1 hour) and 2592000 (30 days). + """ diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py index f84b3ee4a0..f478a92fbb 100644 --- a/src/openai/types/beta/realtime/session.py +++ b/src/openai/types/beta/realtime/session.py @@ -260,7 +260,7 @@ class Session(BaseModel): This can be set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjuction + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index 6be09d8bae..8a477f9843 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -137,7 +137,7 @@ class SessionCreateParams(TypedDict, total=False): This can be set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjuction + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index 5b4185dbf6..11929ab376 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -282,7 +282,7 @@ class Session(BaseModel): This can be set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjuction + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index 3063449bfd..e939f4cc79 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -280,7 +280,7 @@ class Session(TypedDict, total=False): This can be set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjuction + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and diff --git a/src/openai/types/beta/realtime/transcription_session_create_params.py b/src/openai/types/beta/realtime/transcription_session_create_params.py index 15b2f14c14..3ac3af4fa9 100644 --- a/src/openai/types/beta/realtime/transcription_session_create_params.py +++ b/src/openai/types/beta/realtime/transcription_session_create_params.py @@ -61,7 +61,7 @@ class TranscriptionSessionCreateParams(TypedDict, total=False): This can be set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjuction + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and diff --git a/src/openai/types/beta/realtime/transcription_session_update.py b/src/openai/types/beta/realtime/transcription_session_update.py index 73253b6848..5ae1ad226d 100644 --- a/src/openai/types/beta/realtime/transcription_session_update.py +++ b/src/openai/types/beta/realtime/transcription_session_update.py @@ -165,7 +165,7 @@ class Session(BaseModel): This can be set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjuction + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and diff --git a/src/openai/types/beta/realtime/transcription_session_update_param.py b/src/openai/types/beta/realtime/transcription_session_update_param.py index 6b38a9af39..d7065f61c7 100644 --- a/src/openai/types/beta/realtime/transcription_session_update_param.py +++ b/src/openai/types/beta/realtime/transcription_session_update_param.py @@ -165,7 +165,7 @@ class Session(TypedDict, total=False): This can be set to `null` to turn off, in which case the client must manually trigger model response. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjuction + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with "uhhm", the model will score a low probability of turn end and diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index d813710579..ad148d693a 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -169,7 +169,7 @@ class ThreadCreateAndRunParamsBase(TypedDict, total=False): truncation_strategy: Optional[TruncationStrategy] """Controls for how a thread will be truncated prior to the run. - Use this to control the intial context window of the run. + Use this to control the initial context window of the run. """ diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index da9418d6f9..c545cc3759 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -228,7 +228,7 @@ class Run(BaseModel): truncation_strategy: Optional[TruncationStrategy] = None """Controls for how a thread will be truncated prior to the run. - Use this to control the intial context window of the run. + Use this to control the initial context window of the run. """ usage: Optional[Usage] = None diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index f9defcb19c..cfd272f5ad 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -176,7 +176,7 @@ class RunCreateParamsBase(TypedDict, total=False): truncation_strategy: Optional[TruncationStrategy] """Controls for how a thread will be truncated prior to the run. - Use this to control the intial context window of the run. + Use this to control the initial context window of the run. """ diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 42463f7ec8..6bc4bafe79 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -68,9 +68,8 @@ class ChatCompletion(BaseModel): - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index 082bb6cc19..ea32d157ef 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -137,9 +137,8 @@ class ChatCompletionChunk(BaseModel): - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index a3bc90b0a2..3ebab45b56 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -25,6 +25,7 @@ "FunctionCall", "Function", "ResponseFormat", + "Text", "WebSearchOptions", "WebSearchOptionsUserLocation", "WebSearchOptionsUserLocationApproximate", @@ -233,9 +234,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -271,6 +271,8 @@ class CompletionCreateParamsBase(TypedDict, total=False): this or `top_p` but not both. """ + text: Text + tool_choice: ChatCompletionToolChoiceOptionParam """ Controls which (if any) tool is called by the model. `none` means the model will @@ -365,6 +367,16 @@ class Function(TypedDict, total=False): ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] +class Text(TypedDict, total=False): + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ + + class WebSearchOptionsUserLocationApproximate(TypedDict, total=False): city: str """Free text input for the city of the user, e.g. `San Francisco`.""" diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index 728dfd350f..f4583b16a3 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -2,12 +2,12 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from .._types import FileTypes from .file_purpose import FilePurpose -__all__ = ["FileCreateParams"] +__all__ = ["FileCreateParams", "ExpiresAfter"] class FileCreateParams(TypedDict, total=False): @@ -22,3 +22,24 @@ class FileCreateParams(TypedDict, total=False): fine-tuning - `user_data`: Flexible file type for any purpose - `evals`: Used for eval data sets """ + + expires_after: ExpiresAfter + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files + are persisted until they are manually deleted. + """ + + +class ExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["created_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `created_at`. + """ + + seconds: Required[int] + """The number of seconds after the anchor time that the file will expire. + + Must be between 3600 (1 hour) and 2592000 (30 days). + """ diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 74d8688081..72ec741f91 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -42,7 +42,6 @@ from .response_input_param import ResponseInputParam as ResponseInputParam from .response_output_item import ResponseOutputItem as ResponseOutputItem from .response_output_text import ResponseOutputText as ResponseOutputText -from .response_text_config import ResponseTextConfig as ResponseTextConfig from .tool_choice_function import ToolChoiceFunction as ToolChoiceFunction from .response_failed_event import ResponseFailedEvent as ResponseFailedEvent from .response_prompt_param import ResponsePromptParam as ResponsePromptParam @@ -76,7 +75,6 @@ from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam from .response_output_text_param import ResponseOutputTextParam as ResponseOutputTextParam -from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam as ToolChoiceFunctionParam from .response_computer_tool_call import ResponseComputerToolCall as ResponseComputerToolCall from .response_format_text_config import ResponseFormatTextConfig as ResponseFormatTextConfig diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 5ebb18fda4..49e38a46fe 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -18,11 +18,11 @@ from .tool_choice_allowed import ToolChoiceAllowed from .tool_choice_options import ToolChoiceOptions from .response_output_item import ResponseOutputItem -from .response_text_config import ResponseTextConfig from .tool_choice_function import ToolChoiceFunction from ..shared.responses_model import ResponsesModel +from .response_format_text_config import ResponseFormatTextConfig -__all__ = ["Response", "IncompleteDetails", "ToolChoice"] +__all__ = ["Response", "IncompleteDetails", "ToolChoice", "Text"] class IncompleteDetails(BaseModel): @@ -35,6 +35,32 @@ class IncompleteDetails(BaseModel): ] +class Text(BaseModel): + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ + + class Response(BaseModel): id: str """Unique identifier for this Response.""" @@ -177,7 +203,7 @@ class Response(BaseModel): """ reasoning: Optional[Reasoning] = None - """**o-series models only** + """**gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -201,9 +227,8 @@ class Response(BaseModel): - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -219,14 +244,7 @@ class Response(BaseModel): `incomplete`. """ - text: Optional[ResponseTextConfig] = None - """Configuration options for a text response from the model. - - Can be plain text or structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - """ + text: Optional[Text] = None top_logprobs: Optional[int] = None """ diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index ea91fa1265..89afccf06b 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -16,13 +16,14 @@ from ..shared_params.reasoning import Reasoning from .tool_choice_custom_param import ToolChoiceCustomParam from .tool_choice_allowed_param import ToolChoiceAllowedParam -from .response_text_config_param import ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam from ..shared_params.responses_model import ResponsesModel +from .response_format_text_config_param import ResponseFormatTextConfigParam __all__ = [ "ResponseCreateParamsBase", "StreamOptions", + "Text", "ToolChoice", "ResponseCreateParamsNonStreaming", "ResponseCreateParamsStreaming", @@ -134,7 +135,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): """ reasoning: Optional[Reasoning] - """**o-series models only** + """**gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). @@ -158,9 +159,8 @@ class ResponseCreateParamsBase(TypedDict, total=False): - If set to 'default', then the request will be processed with the standard pricing and performance for the selected model. - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or - 'priority', then the request will be processed with the corresponding service - tier. [Contact sales](https://openai.com/contact-sales) to learn more about - Priority processing. + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. - When not set, the default behavior is 'auto'. When the `service_tier` parameter is set, the response body will include the @@ -183,14 +183,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): this or `top_p` but not both. """ - text: ResponseTextConfigParam - """Configuration options for a text response from the model. - - Can be plain text or structured JSON data. Learn more: - - - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - """ + text: Text tool_choice: ToolChoice """ @@ -267,6 +260,32 @@ class StreamOptions(TypedDict, total=False): """ +class Text(TypedDict, total=False): + format: ResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ + + ToolChoice: TypeAlias = Union[ ToolChoiceOptions, ToolChoiceAllowedParam, diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py deleted file mode 100644 index c53546da6d..0000000000 --- a/src/openai/types/responses/response_text_config.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .response_format_text_config import ResponseFormatTextConfig - -__all__ = ["ResponseTextConfig"] - - -class ResponseTextConfig(BaseModel): - format: Optional[ResponseFormatTextConfig] = None - """An object specifying the format that the model must output. - - Configuring `{ "type": "json_schema" }` enables Structured Outputs, which - ensures the model will match your supplied JSON schema. Learn more in the - [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - - The default format is `{ "type": "text" }` with no additional options. - - **Not recommended for gpt-4o and newer models:** - - Setting to `{ "type": "json_object" }` enables the older JSON mode, which - ensures the message the model generates is valid JSON. Using `json_schema` is - preferred for models that support it. - """ - - verbosity: Optional[Literal["low", "medium", "high"]] = None - """Constrains the verbosity of the model's response. - - Lower values will result in more concise responses, while higher values will - result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. - """ diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py deleted file mode 100644 index 1229fce35b..0000000000 --- a/src/openai/types/responses/response_text_config_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, TypedDict - -from .response_format_text_config_param import ResponseFormatTextConfigParam - -__all__ = ["ResponseTextConfigParam"] - - -class ResponseTextConfigParam(TypedDict, total=False): - format: ResponseFormatTextConfigParam - """An object specifying the format that the model must output. - - Configuring `{ "type": "json_schema" }` enables Structured Outputs, which - ensures the model will match your supplied JSON schema. Learn more in the - [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - - The default format is `{ "type": "text" }` with no additional options. - - **Not recommended for gpt-4o and newer models:** - - Setting to `{ "type": "json_object" }` enables the older JSON mode, which - ensures the message the model generates is valid JSON. Using `json_schema` is - preferred for models that support it. - """ - - verbosity: Optional[Literal["low", "medium", "high"]] - """Constrains the verbosity of the model's response. - - Lower values will result in more concise responses, while higher values will - result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. - """ diff --git a/src/openai/types/upload_create_params.py b/src/openai/types/upload_create_params.py index 2ebabe6c66..ab4cded81d 100644 --- a/src/openai/types/upload_create_params.py +++ b/src/openai/types/upload_create_params.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Literal, Required, TypedDict from .file_purpose import FilePurpose -__all__ = ["UploadCreateParams"] +__all__ = ["UploadCreateParams", "ExpiresAfter"] class UploadCreateParams(TypedDict, total=False): @@ -29,3 +29,24 @@ class UploadCreateParams(TypedDict, total=False): See the [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose). """ + + expires_after: ExpiresAfter + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files + are persisted until they are manually deleted. + """ + + +class ExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["created_at"]] + """Anchor timestamp after which the expiration policy applies. + + Supported anchors: `created_at`. + """ + + seconds: Required[int] + """The number of seconds after the anchor time that the file will expire. + + Must be between 3600 (1 hour) and 2592000 (30 days). + """ diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 358ea18cbb..885c3bd9a6 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -86,6 +86,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "include_usage": True, }, temperature=1, + text={"verbosity": "low"}, tool_choice="none", tools=[ { @@ -218,6 +219,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "include_usage": True, }, temperature=1, + text={"verbosity": "low"}, tool_choice="none", tools=[ { @@ -527,6 +529,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "include_usage": True, }, temperature=1, + text={"verbosity": "low"}, tool_choice="none", tools=[ { @@ -659,6 +662,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "include_usage": True, }, temperature=1, + text={"verbosity": "low"}, tool_choice="none", tools=[ { diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py index 6775094a58..95b94c4846 100644 --- a/tests/api_resources/test_batches.py +++ b/tests/api_resources/test_batches.py @@ -34,6 +34,10 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: endpoint="/v1/responses", input_file_id="string", metadata={"foo": "string"}, + output_expires_after={ + "anchor": "created_at", + "seconds": 3600, + }, ) assert_matches_type(Batch, batch, path=["response"]) @@ -196,6 +200,10 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> endpoint="/v1/responses", input_file_id="string", metadata={"foo": "string"}, + output_expires_after={ + "anchor": "created_at", + "seconds": 3600, + }, ) assert_matches_type(Batch, batch, path=["response"]) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index fc4bb4a18e..67c809f155 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -31,6 +31,18 @@ def test_method_create(self, client: OpenAI) -> None: ) assert_matches_type(FileObject, file, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + file = client.files.create( + file=b"raw file contents", + purpose="assistants", + expires_after={ + "anchor": "created_at", + "seconds": 3600, + }, + ) + assert_matches_type(FileObject, file, path=["response"]) + @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.files.with_raw_response.create( @@ -272,6 +284,18 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(FileObject, file, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + file = await async_client.files.create( + file=b"raw file contents", + purpose="assistants", + expires_after={ + "anchor": "created_at", + "seconds": 3600, + }, + ) + assert_matches_type(FileObject, file, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.files.with_raw_response.create( diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 310800b87e..868ab3a4ca 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -10,9 +10,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai._utils import assert_signatures_in_sync -from openai.types.responses import ( - Response, -) +from openai.types.responses import Response base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") diff --git a/tests/api_resources/test_uploads.py b/tests/api_resources/test_uploads.py index 72a2f6c83d..0e438a3c61 100644 --- a/tests/api_resources/test_uploads.py +++ b/tests/api_resources/test_uploads.py @@ -27,6 +27,20 @@ def test_method_create(self, client: OpenAI) -> None: ) assert_matches_type(Upload, upload, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + upload = client.uploads.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + expires_after={ + "anchor": "created_at", + "seconds": 3600, + }, + ) + assert_matches_type(Upload, upload, path=["response"]) + @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.uploads.with_raw_response.create( @@ -162,6 +176,20 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(Upload, upload, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + upload = await async_client.uploads.create( + bytes=0, + filename="filename", + mime_type="mime_type", + purpose="assistants", + expires_after={ + "anchor": "created_at", + "seconds": 3600, + }, + ) + assert_matches_type(Upload, upload, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.uploads.with_raw_response.create( From adb1af8073391a6d58be9c13cfa0664c04d859e2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 05:06:39 +0000 Subject: [PATCH 065/408] release: 1.100.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2dfeb2d9bb..e1f6d3e50c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.99.9" + ".": "1.100.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 392fb8b667..0adb892623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.100.0 (2025-08-18) + +Full Changelog: [v1.99.9...v1.100.0](https://github.com/openai/openai-python/compare/v1.99.9...v1.100.0) + +### Features + +* **api:** add new text parameters, expiration options ([e3dfa7c](https://github.com/openai/openai-python/commit/e3dfa7c417b8c750ff62d98650e75e72ad9b1477)) + ## 1.99.9 (2025-08-12) Full Changelog: [v1.99.8...v1.99.9](https://github.com/openai/openai-python/compare/v1.99.8...v1.99.9) diff --git a/pyproject.toml b/pyproject.toml index ced6079b6d..5fc0396a46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.99.9" +version = "1.100.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7d3b3da5d7..d666729b59 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.99.9" # x-release-please-version +__version__ = "1.100.0" # x-release-please-version From b3547d662e76974b8c6a670eff8c5a05f8bb7f4c Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Mon, 18 Aug 2025 16:35:21 -0400 Subject: [PATCH 066/408] fix(types): revert response text config deletion --- src/openai/types/responses/__init__.py | 2 ++ .../types/responses/response_text_config.py | 35 ++++++++++++++++++ .../responses/response_text_config_param.py | 36 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 src/openai/types/responses/response_text_config.py create mode 100644 src/openai/types/responses/response_text_config_param.py diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 72ec741f91..74d8688081 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -42,6 +42,7 @@ from .response_input_param import ResponseInputParam as ResponseInputParam from .response_output_item import ResponseOutputItem as ResponseOutputItem from .response_output_text import ResponseOutputText as ResponseOutputText +from .response_text_config import ResponseTextConfig as ResponseTextConfig from .tool_choice_function import ToolChoiceFunction as ToolChoiceFunction from .response_failed_event import ResponseFailedEvent as ResponseFailedEvent from .response_prompt_param import ResponsePromptParam as ResponsePromptParam @@ -75,6 +76,7 @@ from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam from .response_output_text_param import ResponseOutputTextParam as ResponseOutputTextParam +from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam as ToolChoiceFunctionParam from .response_computer_tool_call import ResponseComputerToolCall as ResponseComputerToolCall from .response_format_text_config import ResponseFormatTextConfig as ResponseFormatTextConfig diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py new file mode 100644 index 0000000000..c53546da6d --- /dev/null +++ b/src/openai/types/responses/response_text_config.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_format_text_config import ResponseFormatTextConfig + +__all__ = ["ResponseTextConfig"] + + +class ResponseTextConfig(BaseModel): + format: Optional[ResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py new file mode 100644 index 0000000000..1229fce35b --- /dev/null +++ b/src/openai/types/responses/response_text_config_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .response_format_text_config_param import ResponseFormatTextConfigParam + +__all__ = ["ResponseTextConfigParam"] + + +class ResponseTextConfigParam(TypedDict, total=False): + format: ResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ From f889071b8f64739998b7ac31df045881cf5bec62 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 20:40:53 +0000 Subject: [PATCH 067/408] release: 1.100.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e1f6d3e50c..6fb2e7075d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.100.0" + ".": "1.100.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0adb892623..4f3362af2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.100.1 (2025-08-18) + +Full Changelog: [v1.100.0...v1.100.1](https://github.com/openai/openai-python/compare/v1.100.0...v1.100.1) + +### Bug Fixes + +* **types:** revert response text config deletion ([ac4fb19](https://github.com/openai/openai-python/commit/ac4fb1922ae125c8310c30e402932e8bb2976f58)) + ## 1.100.0 (2025-08-18) Full Changelog: [v1.99.9...v1.100.0](https://github.com/openai/openai-python/compare/v1.99.9...v1.100.0) diff --git a/pyproject.toml b/pyproject.toml index 5fc0396a46..a9baee6a55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.100.0" +version = "1.100.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d666729b59..608d190655 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.100.0" # x-release-please-version +__version__ = "1.100.1" # x-release-please-version From a94bd5b239ad73b1f6f7cf11a2fa9d9279096321 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:48:27 +0000 Subject: [PATCH 068/408] chore(api): accurately represent shape for verbosity on Chat Completions --- .stats.yml | 6 +- .../resources/chat/completions/completions.py | 30 ++------- src/openai/resources/responses/responses.py | 65 +++++++++++++++---- .../types/chat/completion_create_params.py | 15 +---- .../types/graders/text_similarity_grader.py | 16 ++++- .../graders/text_similarity_grader_param.py | 16 ++++- src/openai/types/responses/response.py | 39 +++-------- .../types/responses/response_create_params.py | 38 +++-------- tests/api_resources/chat/test_completions.py | 4 -- tests/api_resources/test_responses.py | 4 +- tests/lib/chat/test_completions.py | 2 +- 11 files changed, 110 insertions(+), 125 deletions(-) diff --git a/.stats.yml b/.stats.yml index 66c46e7730..81c991168c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-24be531010b354303d741fc9247c1f84f75978f9f7de68aca92cb4f240a04722.yml -openapi_spec_hash: 3e46f439f6a863beadc71577eb4efa15 -config_hash: ed87b9139ac595a04a2162d754df2fed +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7ef7a457c3bf05364e66e48c9ca34f31bfef1f6c9b7c15b1812346105e0abb16.yml +openapi_spec_hash: a2b1f5d8fbb62175c93b0ebea9f10063 +config_hash: 76afa3236f36854a8705f1281b1990b8 diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index bc5fe0fc05..7e209ff0ee 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -103,7 +103,6 @@ def parse( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -204,7 +203,6 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), - "text": text, "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, @@ -267,7 +265,6 @@ def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -459,7 +456,7 @@ def create( our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - Supports text and image inputs. Note: image inputs over 10MB will be dropped. + Supports text and image inputs. Note: image inputs over 8MB will be dropped. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -556,7 +553,6 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -757,7 +753,7 @@ def create( our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - Supports text and image inputs. Note: image inputs over 10MB will be dropped. + Supports text and image inputs. Note: image inputs over 8MB will be dropped. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -845,7 +841,6 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1046,7 +1041,7 @@ def create( our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - Supports text and image inputs. Note: image inputs over 10MB will be dropped. + Supports text and image inputs. Note: image inputs over 8MB will be dropped. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -1134,7 +1129,6 @@ def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1181,7 +1175,6 @@ def create( "stream": stream, "stream_options": stream_options, "temperature": temperature, - "text": text, "tool_choice": tool_choice, "tools": tools, "top_logprobs": top_logprobs, @@ -1404,7 +1397,6 @@ def stream( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -1475,7 +1467,6 @@ def stream( presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, reasoning_effort=reasoning_effort, - text=text, safety_identifier=safety_identifier, seed=seed, service_tier=service_tier, @@ -1548,7 +1539,6 @@ async def parse( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -1649,7 +1639,6 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prompt_cache_key": prompt_cache_key, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), - "text": text, "safety_identifier": safety_identifier, "seed": seed, "service_tier": service_tier, @@ -1712,7 +1701,6 @@ async def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1904,7 +1892,7 @@ async def create( our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - Supports text and image inputs. Note: image inputs over 10MB will be dropped. + Supports text and image inputs. Note: image inputs over 8MB will be dropped. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -2001,7 +1989,6 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2202,7 +2189,7 @@ async def create( our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - Supports text and image inputs. Note: image inputs over 10MB will be dropped. + Supports text and image inputs. Note: image inputs over 8MB will be dropped. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -2290,7 +2277,6 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2491,7 +2477,7 @@ async def create( our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - Supports text and image inputs. Note: image inputs over 10MB will be dropped. + Supports text and image inputs. Note: image inputs over 8MB will be dropped. stream_options: Options for streaming response. Only set this when you set `stream: true`. @@ -2579,7 +2565,6 @@ async def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2626,7 +2611,6 @@ async def create( "stream": stream, "stream_options": stream_options, "temperature": temperature, - "text": text, "tool_choice": tool_choice, "tools": tools, "top_logprobs": top_logprobs, @@ -2849,7 +2833,6 @@ def stream( presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - text: completion_create_params.Text | NotGiven = NOT_GIVEN, safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, @@ -2921,7 +2904,6 @@ def stream( presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, reasoning_effort=reasoning_effort, - text=text, safety_identifier=safety_identifier, seed=seed, service_tier=service_tier, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 97ad0faa94..375f8b7e71 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -43,6 +43,7 @@ from ...types.responses.response_input_param import ResponseInputParam from ...types.responses.response_prompt_param import ResponsePromptParam from ...types.responses.response_stream_event import ResponseStreamEvent +from ...types.responses.response_text_config_param import ResponseTextConfigParam __all__ = ["Responses", "AsyncResponses"] @@ -94,7 +95,7 @@ def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -238,6 +239,12 @@ def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -315,7 +322,7 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -459,6 +466,12 @@ def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -536,7 +549,7 @@ def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -680,6 +693,12 @@ def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -756,7 +775,7 @@ def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -847,7 +866,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -879,7 +898,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -1008,7 +1027,7 @@ def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1439,7 +1458,7 @@ async def create( stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1583,6 +1602,12 @@ async def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -1660,7 +1685,7 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1804,6 +1829,12 @@ async def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -1881,7 +1912,7 @@ async def create( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2025,6 +2056,12 @@ async def create( focused and deterministic. We generally recommend altering this or `top_p` but not both. + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + tool_choice: How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call. @@ -2101,7 +2138,7 @@ async def create( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2192,7 +2229,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -2224,7 +2261,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -2357,7 +2394,7 @@ async def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: response_create_params.Text | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 3ebab45b56..da37ee4c13 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -25,7 +25,6 @@ "FunctionCall", "Function", "ResponseFormat", - "Text", "WebSearchOptions", "WebSearchOptionsUserLocation", "WebSearchOptionsUserLocationApproximate", @@ -257,7 +256,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): our [model distillation](https://platform.openai.com/docs/guides/distillation) or [evals](https://platform.openai.com/docs/guides/evals) products. - Supports text and image inputs. Note: image inputs over 10MB will be dropped. + Supports text and image inputs. Note: image inputs over 8MB will be dropped. """ stream_options: Optional[ChatCompletionStreamOptionsParam] @@ -271,8 +270,6 @@ class CompletionCreateParamsBase(TypedDict, total=False): this or `top_p` but not both. """ - text: Text - tool_choice: ChatCompletionToolChoiceOptionParam """ Controls which (if any) tool is called by the model. `none` means the model will @@ -367,16 +364,6 @@ class Function(TypedDict, total=False): ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] -class Text(TypedDict, total=False): - verbosity: Optional[Literal["low", "medium", "high"]] - """Constrains the verbosity of the model's response. - - Lower values will result in more concise responses, while higher values will - result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. - """ - - class WebSearchOptionsUserLocationApproximate(TypedDict, total=False): city: str """Free text input for the city of the user, e.g. `San Francisco`.""" diff --git a/src/openai/types/graders/text_similarity_grader.py b/src/openai/types/graders/text_similarity_grader.py index 738d317766..9082ac8969 100644 --- a/src/openai/types/graders/text_similarity_grader.py +++ b/src/openai/types/graders/text_similarity_grader.py @@ -9,12 +9,22 @@ class TextSimilarityGrader(BaseModel): evaluation_metric: Literal[ - "fuzzy_match", "bleu", "gleu", "meteor", "rouge_1", "rouge_2", "rouge_3", "rouge_4", "rouge_5", "rouge_l" + "cosine", + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l", ] """The evaluation metric to use. - One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, - `rouge_4`, `rouge_5`, or `rouge_l`. + One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, + `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. """ input: str diff --git a/src/openai/types/graders/text_similarity_grader_param.py b/src/openai/types/graders/text_similarity_grader_param.py index db14553217..1646afc84b 100644 --- a/src/openai/types/graders/text_similarity_grader_param.py +++ b/src/openai/types/graders/text_similarity_grader_param.py @@ -10,13 +10,23 @@ class TextSimilarityGraderParam(TypedDict, total=False): evaluation_metric: Required[ Literal[ - "fuzzy_match", "bleu", "gleu", "meteor", "rouge_1", "rouge_2", "rouge_3", "rouge_4", "rouge_5", "rouge_l" + "cosine", + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l", ] ] """The evaluation metric to use. - One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, - `rouge_4`, `rouge_5`, or `rouge_l`. + One of `cosine`, `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, + `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`. """ input: Required[str] diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 49e38a46fe..49f60bbc5c 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -18,11 +18,11 @@ from .tool_choice_allowed import ToolChoiceAllowed from .tool_choice_options import ToolChoiceOptions from .response_output_item import ResponseOutputItem +from .response_text_config import ResponseTextConfig from .tool_choice_function import ToolChoiceFunction from ..shared.responses_model import ResponsesModel -from .response_format_text_config import ResponseFormatTextConfig -__all__ = ["Response", "IncompleteDetails", "ToolChoice", "Text"] +__all__ = ["Response", "IncompleteDetails", "ToolChoice"] class IncompleteDetails(BaseModel): @@ -35,32 +35,6 @@ class IncompleteDetails(BaseModel): ] -class Text(BaseModel): - format: Optional[ResponseFormatTextConfig] = None - """An object specifying the format that the model must output. - - Configuring `{ "type": "json_schema" }` enables Structured Outputs, which - ensures the model will match your supplied JSON schema. Learn more in the - [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - - The default format is `{ "type": "text" }` with no additional options. - - **Not recommended for gpt-4o and newer models:** - - Setting to `{ "type": "json_object" }` enables the older JSON mode, which - ensures the message the model generates is valid JSON. Using `json_schema` is - preferred for models that support it. - """ - - verbosity: Optional[Literal["low", "medium", "high"]] = None - """Constrains the verbosity of the model's response. - - Lower values will result in more concise responses, while higher values will - result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. - """ - - class Response(BaseModel): id: str """Unique identifier for this Response.""" @@ -244,7 +218,14 @@ class Response(BaseModel): `incomplete`. """ - text: Optional[Text] = None + text: Optional[ResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ top_logprobs: Optional[int] = None """ diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 89afccf06b..0cd761fcf0 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -16,14 +16,13 @@ from ..shared_params.reasoning import Reasoning from .tool_choice_custom_param import ToolChoiceCustomParam from .tool_choice_allowed_param import ToolChoiceAllowedParam +from .response_text_config_param import ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam from ..shared_params.responses_model import ResponsesModel -from .response_format_text_config_param import ResponseFormatTextConfigParam __all__ = [ "ResponseCreateParamsBase", "StreamOptions", - "Text", "ToolChoice", "ResponseCreateParamsNonStreaming", "ResponseCreateParamsStreaming", @@ -183,7 +182,14 @@ class ResponseCreateParamsBase(TypedDict, total=False): this or `top_p` but not both. """ - text: Text + text: ResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ tool_choice: ToolChoice """ @@ -260,32 +266,6 @@ class StreamOptions(TypedDict, total=False): """ -class Text(TypedDict, total=False): - format: ResponseFormatTextConfigParam - """An object specifying the format that the model must output. - - Configuring `{ "type": "json_schema" }` enables Structured Outputs, which - ensures the model will match your supplied JSON schema. Learn more in the - [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). - - The default format is `{ "type": "text" }` with no additional options. - - **Not recommended for gpt-4o and newer models:** - - Setting to `{ "type": "json_object" }` enables the older JSON mode, which - ensures the message the model generates is valid JSON. Using `json_schema` is - preferred for models that support it. - """ - - verbosity: Optional[Literal["low", "medium", "high"]] - """Constrains the verbosity of the model's response. - - Lower values will result in more concise responses, while higher values will - result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. - """ - - ToolChoice: TypeAlias = Union[ ToolChoiceOptions, ToolChoiceAllowedParam, diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 885c3bd9a6..358ea18cbb 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -86,7 +86,6 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "include_usage": True, }, temperature=1, - text={"verbosity": "low"}, tool_choice="none", tools=[ { @@ -219,7 +218,6 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "include_usage": True, }, temperature=1, - text={"verbosity": "low"}, tool_choice="none", tools=[ { @@ -529,7 +527,6 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "include_usage": True, }, temperature=1, - text={"verbosity": "low"}, tool_choice="none", tools=[ { @@ -662,7 +659,6 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "include_usage": True, }, temperature=1, - text={"verbosity": "low"}, tool_choice="none", tools=[ { diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 868ab3a4ca..310800b87e 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -10,7 +10,9 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai._utils import assert_signatures_in_sync -from openai.types.responses import Response +from openai.types.responses import ( + Response, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index f04a0e3782..f69bc09ca3 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -541,7 +541,7 @@ class Location(BaseModel): content_snapshot=snapshot( '{"id": "chatcmpl-ABfvvX7eB1KsfeZj8VcF3z7G7SbaA", "object": "chat.completion", "created": 1727346163, "model": "gpt-4o-2024-08-06", "choices": [{"index": 0, "message": {"role": "assistant", "content": "{\\"", "refusal": null}, "logprobs": null, "finish_reason": "length"}], "usage": {"prompt_tokens": 79, "completion_tokens": 1, "total_tokens": 80, "completion_tokens_details": {"reasoning_tokens": 0}}, "system_fingerprint": "fp_7568d46099"}' ), - path="/chat/completions", + path="/chat/completions", mock_client=client, respx_mock=respx_mock, ) From 4ada66f8f86473f342aa032ed021b62180422dc1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 14:10:47 +0000 Subject: [PATCH 069/408] release: 1.100.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6fb2e7075d..8910831376 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.100.1" + ".": "1.100.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f3362af2f..2254a59f75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.100.2 (2025-08-19) + +Full Changelog: [v1.100.1...v1.100.2](https://github.com/openai/openai-python/compare/v1.100.1...v1.100.2) + +### Chores + +* **api:** accurately represent shape for verbosity on Chat Completions ([c39d5fd](https://github.com/openai/openai-python/commit/c39d5fd3f5429c6d41f257669a1dd4c67a477455)) + ## 1.100.1 (2025-08-18) Full Changelog: [v1.100.0...v1.100.1](https://github.com/openai/openai-python/compare/v1.100.0...v1.100.1) diff --git a/pyproject.toml b/pyproject.toml index a9baee6a55..c8c3d2fd2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.100.1" +version = "1.100.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 608d190655..29840a21b8 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.100.1" # x-release-please-version +__version__ = "1.100.2" # x-release-please-version From 72e0ad60f0a6cb2c7d39651c7217b3dd1e86315b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:38:10 +0000 Subject: [PATCH 070/408] chore(internal/ci): setup breaking change detection --- .github/workflows/detect-breaking-changes.yml | 42 ++++++++++ .stats.yml | 2 +- pyproject.toml | 1 + requirements-dev.lock | 3 + scripts/detect-breaking-changes | 24 ++++++ scripts/detect-breaking-changes.py | 79 +++++++++++++++++++ 6 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/detect-breaking-changes.yml create mode 100755 scripts/detect-breaking-changes create mode 100644 scripts/detect-breaking-changes.py diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml new file mode 100644 index 0000000000..f10fdf3b19 --- /dev/null +++ b/.github/workflows/detect-breaking-changes.yml @@ -0,0 +1,42 @@ +name: CI +on: + pull_request: + branches: + - main + - next + +jobs: + detect_breaking_changes: + runs-on: 'ubuntu-latest' + name: detect-breaking-changes + if: github.repository == 'openai/openai-python' + steps: + - name: Calculate fetch-depth + run: | + echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + with: + # Ensure we can check out the pull request base in the script below. + fetch-depth: ${{ env.FETCH_DEPTH }} + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + - name: Install dependencies + run: | + rye sync --all-features + - name: Detect removed symbols + run: | + rye run python scripts/detect-breaking-changes.py "${{ github.event.pull_request.base.sha }}" + + - name: Detect breaking changes + run: | + # Try to check out previous versions of the breaking change detection script. This ensures that + # we still detect breaking changes when entire files and their tests are removed. + git checkout "${{ github.event.pull_request.base.sha }}" -- ./scripts/detect-breaking-changes 2>/dev/null || true + ./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 81c991168c..d4994342f7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 111 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7ef7a457c3bf05364e66e48c9ca34f31bfef1f6c9b7c15b1812346105e0abb16.yml openapi_spec_hash: a2b1f5d8fbb62175c93b0ebea9f10063 -config_hash: 76afa3236f36854a8705f1281b1990b8 +config_hash: 4870312b04f48fd717ea4151053e7fb9 diff --git a/pyproject.toml b/pyproject.toml index c8c3d2fd2b..eb1f588896 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,7 @@ dev-dependencies = [ "trio >=0.22.2", "nest_asyncio==1.6.0", "pytest-xdist>=3.6.1", + "griffe>=1", ] [tool.rye.scripts] diff --git a/requirements-dev.lock b/requirements-dev.lock index b1886e036f..e619cb6b64 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -44,6 +44,8 @@ cffi==1.16.0 # via sounddevice charset-normalizer==3.3.2 # via requests +colorama==0.4.6 + # via griffe colorlog==6.7.0 # via nox cryptography==42.0.7 @@ -68,6 +70,7 @@ filelock==3.12.4 frozenlist==1.7.0 # via aiohttp # via aiosignal +griffe==1.12.1 h11==0.16.0 # via httpcore httpcore==1.0.9 diff --git a/scripts/detect-breaking-changes b/scripts/detect-breaking-changes new file mode 100755 index 0000000000..833872ef3a --- /dev/null +++ b/scripts/detect-breaking-changes @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Detecting breaking changes" + +TEST_PATHS=( + tests/api_resources + tests/test_client.py + tests/test_response.py + tests/test_legacy_response.py +) + +for PATHSPEC in "${TEST_PATHS[@]}"; do + # Try to check out previous versions of the test files + # with the current SDK. + git checkout "$1" -- "${PATHSPEC}" 2>/dev/null || true +done + +# Instead of running the tests, use the linter to check if an +# older test is no longer compatible with the latest SDK. +./scripts/lint diff --git a/scripts/detect-breaking-changes.py b/scripts/detect-breaking-changes.py new file mode 100644 index 0000000000..3a30f3db2f --- /dev/null +++ b/scripts/detect-breaking-changes.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import sys +from typing import Iterator +from pathlib import Path + +import rich +import griffe +from rich.text import Text +from rich.style import Style + + +def public_members(obj: griffe.Object | griffe.Alias) -> dict[str, griffe.Object | griffe.Alias]: + if isinstance(obj, griffe.Alias): + # ignore imports for now, they're technically part of the public API + # but we don't have good preventative measures in place to prevent + # changing them + return {} + + return {name: value for name, value in obj.all_members.items() if not name.startswith("_")} + + +def find_breaking_changes( + new_obj: griffe.Object | griffe.Alias, + old_obj: griffe.Object | griffe.Alias, + *, + path: list[str], +) -> Iterator[Text | str]: + new_members = public_members(new_obj) + old_members = public_members(old_obj) + + for name, old_member in old_members.items(): + if isinstance(old_member, griffe.Alias) and len(path) > 2: + # ignore imports in `/types/` for now, they're technically part of the public API + # but we don't have good preventative measures in place to prevent changing them + continue + + new_member = new_members.get(name) + if new_member is None: + cls_name = old_member.__class__.__name__ + yield Text(f"({cls_name})", style=Style(color="rgb(119, 119, 119)")) + yield from [" " for _ in range(10 - len(cls_name))] + yield f" {'.'.join(path)}.{name}" + yield "\n" + continue + + yield from find_breaking_changes(new_member, old_member, path=[*path, name]) + + +def main() -> None: + try: + against_ref = sys.argv[1] + except IndexError as err: + raise RuntimeError("You must specify a base ref to run breaking change detection against") from err + + package = griffe.load( + "openai", + search_paths=[Path(__file__).parent.parent.joinpath("src")], + ) + old_package = griffe.load_git( + "openai", + ref=against_ref, + search_paths=["src"], + ) + assert isinstance(package, griffe.Module) + assert isinstance(old_package, griffe.Module) + + output = list(find_breaking_changes(package, old_package, path=["openai"])) + if output: + rich.print(Text("Breaking changes detected!", style=Style(color="rgb(165, 79, 87)"))) + rich.print() + + for text in output: + rich.print(text, end="") + + sys.exit(1) + + +main() From e328fb4d79badc7ca28a1f599a56ab43eb420363 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 05:04:00 +0000 Subject: [PATCH 071/408] release: 1.100.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8910831376..f3cdcd790c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.100.2" + ".": "1.100.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2254a59f75..c2f89cb09b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.100.3 (2025-08-20) + +Full Changelog: [v1.100.2...v1.100.3](https://github.com/openai/openai-python/compare/v1.100.2...v1.100.3) + +### Chores + +* **internal/ci:** setup breaking change detection ([ca2f936](https://github.com/openai/openai-python/commit/ca2f93600238e875f26395faf6afbefaf15b7c97)) + ## 1.100.2 (2025-08-19) Full Changelog: [v1.100.1...v1.100.2](https://github.com/openai/openai-python/compare/v1.100.1...v1.100.2) diff --git a/pyproject.toml b/pyproject.toml index eb1f588896..4d1055bfce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.100.2" +version = "1.100.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 29840a21b8..9881b45247 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.100.2" # x-release-please-version +__version__ = "1.100.3" # x-release-please-version From 4e28a424e6afd60040e3bdf7c76eebb63bc0c407 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 16:10:05 -0500 Subject: [PATCH 072/408] release: 1.101.0 (#2577) * feat(api): adding support for /v1/conversations to the API * chore: update github action * feat(api): Add connectors support for MCP tool * release: 1.101.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 +- .release-please-manifest.json | 2 +- .stats.yml | 8 +- CHANGELOG.md | 14 + api.md | 49 ++ pyproject.toml | 2 +- src/openai/__init__.py | 1 + src/openai/_client.py | 38 ++ src/openai/_module_client.py | 8 + src/openai/_version.py | 2 +- src/openai/pagination.py | 67 ++- .../resources/conversations/__init__.py | 33 ++ .../resources/conversations/conversations.py | 474 +++++++++++++++ src/openai/resources/conversations/items.py | 553 ++++++++++++++++++ src/openai/resources/responses/input_items.py | 8 - src/openai/resources/responses/responses.py | 60 +- src/openai/types/conversations/__init__.py | 27 + .../computer_screenshot_content.py | 22 + .../container_file_citation_body.py | 27 + .../types/conversations/conversation.py | 30 + .../conversation_create_params.py | 26 + .../conversation_deleted_resource.py | 15 + .../types/conversations/conversation_item.py | 209 +++++++ .../conversations/conversation_item_list.py | 26 + .../conversation_update_params.py | 19 + .../types/conversations/file_citation_body.py | 21 + .../types/conversations/input_file_content.py | 22 + .../conversations/input_image_content.py | 28 + .../types/conversations/input_text_content.py | 15 + .../types/conversations/item_create_params.py | 24 + .../types/conversations/item_list_params.py | 48 ++ .../conversations/item_retrieve_params.py | 22 + src/openai/types/conversations/lob_prob.py | 18 + src/openai/types/conversations/message.py | 56 ++ .../conversations/output_text_content.py | 30 + .../types/conversations/refusal_content.py | 15 + .../conversations/summary_text_content.py | 13 + .../types/conversations/text_content.py | 13 + .../types/conversations/top_log_prob.py | 15 + .../types/conversations/url_citation_body.py | 24 + ...create_eval_completions_run_data_source.py | 26 +- ..._eval_completions_run_data_source_param.py | 24 +- src/openai/types/responses/__init__.py | 1 + .../types/responses/input_item_list_params.py | 3 - src/openai/types/responses/response.py | 15 +- .../responses/response_conversation_param.py | 12 + .../types/responses/response_create_params.py | 14 + src/openai/types/responses/tool.py | 84 ++- src/openai/types/responses/tool_param.py | 82 ++- tests/api_resources/conversations/__init__.py | 1 + .../api_resources/conversations/test_items.py | 491 ++++++++++++++++ .../responses/test_input_items.py | 2 - tests/api_resources/test_conversations.py | 341 +++++++++++ tests/api_resources/test_responses.py | 4 + 54 files changed, 3114 insertions(+), 74 deletions(-) create mode 100644 src/openai/resources/conversations/__init__.py create mode 100644 src/openai/resources/conversations/conversations.py create mode 100644 src/openai/resources/conversations/items.py create mode 100644 src/openai/types/conversations/__init__.py create mode 100644 src/openai/types/conversations/computer_screenshot_content.py create mode 100644 src/openai/types/conversations/container_file_citation_body.py create mode 100644 src/openai/types/conversations/conversation.py create mode 100644 src/openai/types/conversations/conversation_create_params.py create mode 100644 src/openai/types/conversations/conversation_deleted_resource.py create mode 100644 src/openai/types/conversations/conversation_item.py create mode 100644 src/openai/types/conversations/conversation_item_list.py create mode 100644 src/openai/types/conversations/conversation_update_params.py create mode 100644 src/openai/types/conversations/file_citation_body.py create mode 100644 src/openai/types/conversations/input_file_content.py create mode 100644 src/openai/types/conversations/input_image_content.py create mode 100644 src/openai/types/conversations/input_text_content.py create mode 100644 src/openai/types/conversations/item_create_params.py create mode 100644 src/openai/types/conversations/item_list_params.py create mode 100644 src/openai/types/conversations/item_retrieve_params.py create mode 100644 src/openai/types/conversations/lob_prob.py create mode 100644 src/openai/types/conversations/message.py create mode 100644 src/openai/types/conversations/output_text_content.py create mode 100644 src/openai/types/conversations/refusal_content.py create mode 100644 src/openai/types/conversations/summary_text_content.py create mode 100644 src/openai/types/conversations/text_content.py create mode 100644 src/openai/types/conversations/top_log_prob.py create mode 100644 src/openai/types/conversations/url_citation_body.py create mode 100644 src/openai/types/responses/response_conversation_param.py create mode 100644 tests/api_resources/conversations/__init__.py create mode 100644 tests/api_resources/conversations/test_items.py create mode 100644 tests/api_resources/test_conversations.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8067386d5f..5e56aae09a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: run: ./scripts/lint build: - if: github.repository == 'stainless-sdks/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork timeout-minutes: 10 name: build permissions: @@ -61,12 +61,14 @@ jobs: run: rye build - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/openai-python' id: github-oidc uses: actions/github-script@v6 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball + if: github.repository == 'stainless-sdks/openai-python' env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f3cdcd790c..070375331a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.100.3" + ".": "1.101.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d4994342f7..f2d5304a5b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 111 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7ef7a457c3bf05364e66e48c9ca34f31bfef1f6c9b7c15b1812346105e0abb16.yml -openapi_spec_hash: a2b1f5d8fbb62175c93b0ebea9f10063 -config_hash: 4870312b04f48fd717ea4151053e7fb9 +configured_endpoints: 119 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-ddbdf9343316047e8a773c54fb24e4a8d225955e202a1888fde6f9c8898ebf98.yml +openapi_spec_hash: 9802f6dd381558466c897f6e387e06ca +config_hash: fe0ea26680ac2075a6cd66416aefe7db diff --git a/CHANGELOG.md b/CHANGELOG.md index c2f89cb09b..44b25e0a4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 1.101.0 (2025-08-21) + +Full Changelog: [v1.100.3...v1.101.0](https://github.com/openai/openai-python/compare/v1.100.3...v1.101.0) + +### Features + +* **api:** Add connectors support for MCP tool ([a47f962](https://github.com/openai/openai-python/commit/a47f962daf579c142b8af5579be732772b688a29)) +* **api:** adding support for /v1/conversations to the API ([e30bcbc](https://github.com/openai/openai-python/commit/e30bcbc0cb7c827af779bee6971f976261abfb67)) + + +### Chores + +* update github action ([7333b28](https://github.com/openai/openai-python/commit/7333b282718a5f6977f30e1a2548207b3a089bd4)) + ## 1.100.3 (2025-08-20) Full Changelog: [v1.100.2...v1.100.3](https://github.com/openai/openai-python/compare/v1.100.2...v1.100.3) diff --git a/api.md b/api.md index 92b068b134..7eb62e67f2 100644 --- a/api.md +++ b/api.md @@ -751,6 +751,7 @@ from openai.types.responses import ( ResponseContent, ResponseContentPartAddedEvent, ResponseContentPartDoneEvent, + ResponseConversationParam, ResponseCreatedEvent, ResponseCustomToolCall, ResponseCustomToolCallInputDeltaEvent, @@ -854,6 +855,54 @@ Methods: - client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] +# Conversations + +Types: + +```python +from openai.types.conversations import ( + ComputerScreenshotContent, + ContainerFileCitationBody, + Conversation, + ConversationDeleted, + ConversationDeletedResource, + FileCitationBody, + InputFileContent, + InputImageContent, + InputTextContent, + LobProb, + Message, + OutputTextContent, + RefusalContent, + SummaryTextContent, + TextContent, + TopLogProb, + URLCitationBody, +) +``` + +Methods: + +- client.conversations.create(\*\*params) -> Conversation +- client.conversations.retrieve(conversation_id) -> Conversation +- client.conversations.update(conversation_id, \*\*params) -> Conversation +- client.conversations.delete(conversation_id) -> ConversationDeletedResource + +## Items + +Types: + +```python +from openai.types.conversations import ConversationItem, ConversationItemList +``` + +Methods: + +- client.conversations.items.create(conversation_id, \*\*params) -> ConversationItemList +- client.conversations.items.retrieve(item_id, \*, conversation_id, \*\*params) -> ConversationItem +- client.conversations.items.list(conversation_id, \*\*params) -> SyncConversationCursorPage[ConversationItem] +- client.conversations.items.delete(item_id, \*, conversation_id) -> Conversation + # Evals Types: diff --git a/pyproject.toml b/pyproject.toml index 4d1055bfce..8198b178be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.100.3" +version = "1.101.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 226fed9554..b944fbed5e 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -386,5 +386,6 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] completions as completions, fine_tuning as fine_tuning, moderations as moderations, + conversations as conversations, vector_stores as vector_stores, ) diff --git a/src/openai/_client.py b/src/openai/_client.py index ed9b46f4b0..b99db786a7 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -51,6 +51,7 @@ completions, fine_tuning, moderations, + conversations, vector_stores, ) from .resources.files import Files, AsyncFiles @@ -69,6 +70,7 @@ from .resources.responses.responses import Responses, AsyncResponses from .resources.containers.containers import Containers, AsyncContainers from .resources.fine_tuning.fine_tuning import FineTuning, AsyncFineTuning + from .resources.conversations.conversations import Conversations, AsyncConversations from .resources.vector_stores.vector_stores import VectorStores, AsyncVectorStores __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "OpenAI", "AsyncOpenAI", "Client", "AsyncClient"] @@ -254,6 +256,12 @@ def responses(self) -> Responses: return Responses(self) + @cached_property + def conversations(self) -> Conversations: + from .resources.conversations import Conversations + + return Conversations(self) + @cached_property def evals(self) -> Evals: from .resources.evals import Evals @@ -573,6 +581,12 @@ def responses(self) -> AsyncResponses: return AsyncResponses(self) + @cached_property + def conversations(self) -> AsyncConversations: + from .resources.conversations import AsyncConversations + + return AsyncConversations(self) + @cached_property def evals(self) -> AsyncEvals: from .resources.evals import AsyncEvals @@ -802,6 +816,12 @@ def responses(self) -> responses.ResponsesWithRawResponse: return ResponsesWithRawResponse(self._client.responses) + @cached_property + def conversations(self) -> conversations.ConversationsWithRawResponse: + from .resources.conversations import ConversationsWithRawResponse + + return ConversationsWithRawResponse(self._client.conversations) + @cached_property def evals(self) -> evals.EvalsWithRawResponse: from .resources.evals import EvalsWithRawResponse @@ -905,6 +925,12 @@ def responses(self) -> responses.AsyncResponsesWithRawResponse: return AsyncResponsesWithRawResponse(self._client.responses) + @cached_property + def conversations(self) -> conversations.AsyncConversationsWithRawResponse: + from .resources.conversations import AsyncConversationsWithRawResponse + + return AsyncConversationsWithRawResponse(self._client.conversations) + @cached_property def evals(self) -> evals.AsyncEvalsWithRawResponse: from .resources.evals import AsyncEvalsWithRawResponse @@ -1008,6 +1034,12 @@ def responses(self) -> responses.ResponsesWithStreamingResponse: return ResponsesWithStreamingResponse(self._client.responses) + @cached_property + def conversations(self) -> conversations.ConversationsWithStreamingResponse: + from .resources.conversations import ConversationsWithStreamingResponse + + return ConversationsWithStreamingResponse(self._client.conversations) + @cached_property def evals(self) -> evals.EvalsWithStreamingResponse: from .resources.evals import EvalsWithStreamingResponse @@ -1111,6 +1143,12 @@ def responses(self) -> responses.AsyncResponsesWithStreamingResponse: return AsyncResponsesWithStreamingResponse(self._client.responses) + @cached_property + def conversations(self) -> conversations.AsyncConversationsWithStreamingResponse: + from .resources.conversations import AsyncConversationsWithStreamingResponse + + return AsyncConversationsWithStreamingResponse(self._client.conversations) + @cached_property def evals(self) -> evals.AsyncEvalsWithStreamingResponse: from .resources.evals import AsyncEvalsWithStreamingResponse diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index a80e939300..5c8df24014 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -22,6 +22,7 @@ from .resources.responses.responses import Responses from .resources.containers.containers import Containers from .resources.fine_tuning.fine_tuning import FineTuning + from .resources.conversations.conversations import Conversations from .resources.vector_stores.vector_stores import VectorStores from . import _load_client @@ -130,6 +131,12 @@ def __load__(self) -> VectorStores: return _load_client().vector_stores +class ConversationsProxy(LazyProxy["Conversations"]): + @override + def __load__(self) -> Conversations: + return _load_client().conversations + + chat: Chat = ChatProxy().__as_proxied__() beta: Beta = BetaProxy().__as_proxied__() files: Files = FilesProxy().__as_proxied__() @@ -147,3 +154,4 @@ def __load__(self) -> VectorStores: moderations: Moderations = ModerationsProxy().__as_proxied__() fine_tuning: FineTuning = FineTuningProxy().__as_proxied__() vector_stores: VectorStores = VectorStoresProxy().__as_proxied__() +conversations: Conversations = ConversationsProxy().__as_proxied__() diff --git a/src/openai/_version.py b/src/openai/_version.py index 9881b45247..802084af5d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.100.3" # x-release-please-version +__version__ = "1.101.0" # x-release-please-version diff --git a/src/openai/pagination.py b/src/openai/pagination.py index a59cced854..4dd3788aa3 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -5,7 +5,14 @@ from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage -__all__ = ["SyncPage", "AsyncPage", "SyncCursorPage", "AsyncCursorPage"] +__all__ = [ + "SyncPage", + "AsyncPage", + "SyncCursorPage", + "AsyncCursorPage", + "SyncConversationCursorPage", + "AsyncConversationCursorPage", +] _T = TypeVar("_T") @@ -123,3 +130,61 @@ def next_page_info(self) -> Optional[PageInfo]: return None return PageInfo(params={"after": item.id}) + + +class SyncConversationCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + last_id: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_id = self.last_id + if not last_id: + return None + + return PageInfo(params={"after": last_id}) + + +class AsyncConversationCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + last_id: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + last_id = self.last_id + if not last_id: + return None + + return PageInfo(params={"after": last_id}) diff --git a/src/openai/resources/conversations/__init__.py b/src/openai/resources/conversations/__init__.py new file mode 100644 index 0000000000..c6c4fd6ee4 --- /dev/null +++ b/src/openai/resources/conversations/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .items import ( + Items, + AsyncItems, + ItemsWithRawResponse, + AsyncItemsWithRawResponse, + ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse, +) +from .conversations import ( + Conversations, + AsyncConversations, + ConversationsWithRawResponse, + AsyncConversationsWithRawResponse, + ConversationsWithStreamingResponse, + AsyncConversationsWithStreamingResponse, +) + +__all__ = [ + "Items", + "AsyncItems", + "ItemsWithRawResponse", + "AsyncItemsWithRawResponse", + "ItemsWithStreamingResponse", + "AsyncItemsWithStreamingResponse", + "Conversations", + "AsyncConversations", + "ConversationsWithRawResponse", + "AsyncConversationsWithRawResponse", + "ConversationsWithStreamingResponse", + "AsyncConversationsWithStreamingResponse", +] diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py new file mode 100644 index 0000000000..13bc1fb1ce --- /dev/null +++ b/src/openai/resources/conversations/conversations.py @@ -0,0 +1,474 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable, Optional + +import httpx + +from ... import _legacy_response +from .items import ( + Items, + AsyncItems, + ItemsWithRawResponse, + AsyncItemsWithRawResponse, + ItemsWithStreamingResponse, + AsyncItemsWithStreamingResponse, +) +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.conversations import conversation_create_params, conversation_update_params +from ...types.shared_params.metadata import Metadata +from ...types.conversations.conversation import Conversation +from ...types.responses.response_input_item_param import ResponseInputItemParam +from ...types.conversations.conversation_deleted_resource import ConversationDeletedResource + +__all__ = ["Conversations", "AsyncConversations"] + + +class Conversations(SyncAPIResource): + @cached_property + def items(self) -> Items: + return Items(self._client) + + @cached_property + def with_raw_response(self) -> ConversationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ConversationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ConversationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ConversationsWithStreamingResponse(self) + + def create( + self, + *, + items: Optional[Iterable[ResponseInputItemParam]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Create a conversation with the given ID. + + Args: + items: Initial items to include in the conversation context. You may add up to 20 items + at a time. + + metadata: Set of 16 key-value pairs that can be attached to an object. Useful for storing + additional information about the object in a structured format. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/conversations", + body=maybe_transform( + { + "items": items, + "metadata": metadata, + }, + conversation_create_params.ConversationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + def retrieve( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Get a conversation with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._get( + f"/conversations/{conversation_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + def update( + self, + conversation_id: str, + *, + metadata: Dict[str, str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Update a conversation's metadata with the given ID. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._post( + f"/conversations/{conversation_id}", + body=maybe_transform({"metadata": metadata}, conversation_update_params.ConversationUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + def delete( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ConversationDeletedResource: + """ + Delete a conversation with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._delete( + f"/conversations/{conversation_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationDeletedResource, + ) + + +class AsyncConversations(AsyncAPIResource): + @cached_property + def items(self) -> AsyncItems: + return AsyncItems(self._client) + + @cached_property + def with_raw_response(self) -> AsyncConversationsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncConversationsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncConversationsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncConversationsWithStreamingResponse(self) + + async def create( + self, + *, + items: Optional[Iterable[ResponseInputItemParam]] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Create a conversation with the given ID. + + Args: + items: Initial items to include in the conversation context. You may add up to 20 items + at a time. + + metadata: Set of 16 key-value pairs that can be attached to an object. Useful for storing + additional information about the object in a structured format. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/conversations", + body=await async_maybe_transform( + { + "items": items, + "metadata": metadata, + }, + conversation_create_params.ConversationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + async def retrieve( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Get a conversation with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._get( + f"/conversations/{conversation_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + async def update( + self, + conversation_id: str, + *, + metadata: Dict[str, str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Update a conversation's metadata with the given ID. + + Args: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. Keys are strings with a maximum + length of 64 characters. Values are strings with a maximum length of 512 + characters. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._post( + f"/conversations/{conversation_id}", + body=await async_maybe_transform( + {"metadata": metadata}, conversation_update_params.ConversationUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + async def delete( + self, + conversation_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ConversationDeletedResource: + """ + Delete a conversation with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._delete( + f"/conversations/{conversation_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ConversationDeletedResource, + ) + + +class ConversationsWithRawResponse: + def __init__(self, conversations: Conversations) -> None: + self._conversations = conversations + + self.create = _legacy_response.to_raw_response_wrapper( + conversations.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + conversations.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + conversations.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> ItemsWithRawResponse: + return ItemsWithRawResponse(self._conversations.items) + + +class AsyncConversationsWithRawResponse: + def __init__(self, conversations: AsyncConversations) -> None: + self._conversations = conversations + + self.create = _legacy_response.async_to_raw_response_wrapper( + conversations.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + conversations.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + conversations.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> AsyncItemsWithRawResponse: + return AsyncItemsWithRawResponse(self._conversations.items) + + +class ConversationsWithStreamingResponse: + def __init__(self, conversations: Conversations) -> None: + self._conversations = conversations + + self.create = to_streamed_response_wrapper( + conversations.create, + ) + self.retrieve = to_streamed_response_wrapper( + conversations.retrieve, + ) + self.update = to_streamed_response_wrapper( + conversations.update, + ) + self.delete = to_streamed_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> ItemsWithStreamingResponse: + return ItemsWithStreamingResponse(self._conversations.items) + + +class AsyncConversationsWithStreamingResponse: + def __init__(self, conversations: AsyncConversations) -> None: + self._conversations = conversations + + self.create = async_to_streamed_response_wrapper( + conversations.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + conversations.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + conversations.update, + ) + self.delete = async_to_streamed_response_wrapper( + conversations.delete, + ) + + @cached_property + def items(self) -> AsyncItemsWithStreamingResponse: + return AsyncItemsWithStreamingResponse(self._conversations.items) diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py new file mode 100644 index 0000000000..1e696a79ed --- /dev/null +++ b/src/openai/resources/conversations/items.py @@ -0,0 +1,553 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, List, Iterable, cast +from typing_extensions import Literal + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.conversations import item_list_params, item_create_params, item_retrieve_params +from ...types.conversations.conversation import Conversation +from ...types.responses.response_includable import ResponseIncludable +from ...types.conversations.conversation_item import ConversationItem +from ...types.responses.response_input_item_param import ResponseInputItemParam +from ...types.conversations.conversation_item_list import ConversationItemList + +__all__ = ["Items", "AsyncItems"] + + +class Items(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ItemsWithStreamingResponse(self) + + def create( + self, + conversation_id: str, + *, + items: Iterable[ResponseInputItemParam], + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ConversationItemList: + """ + Create items in a conversation with the given ID. + + Args: + items: The items to add to the conversation. You may add up to 20 items at a time. + + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._post( + f"/conversations/{conversation_id}/items", + body=maybe_transform({"items": items}, item_create_params.ItemCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, item_create_params.ItemCreateParams), + ), + cast_to=ConversationItemList, + ) + + def retrieve( + self, + item_id: str, + *, + conversation_id: str, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ConversationItem: + """ + Get a single item from a conversation with the given IDs. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return cast( + ConversationItem, + self._get( + f"/conversations/{conversation_id}/items/{item_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, item_retrieve_params.ItemRetrieveParams), + ), + cast_to=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ), + ) + + def list( + self, + conversation_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncConversationCursorPage[ConversationItem]: + """ + List all items for a conversation with the given ID. + + Args: + after: An item ID to list items after, used in pagination. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._get_api_list( + f"/conversations/{conversation_id}/items", + page=SyncConversationCursorPage[ConversationItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + ), + model=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ) + + def delete( + self, + item_id: str, + *, + conversation_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Delete an item from a conversation with the given IDs. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return self._delete( + f"/conversations/{conversation_id}/items/{item_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + +class AsyncItems(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncItemsWithStreamingResponse(self) + + async def create( + self, + conversation_id: str, + *, + items: Iterable[ResponseInputItemParam], + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ConversationItemList: + """ + Create items in a conversation with the given ID. + + Args: + items: The items to add to the conversation. You may add up to 20 items at a time. + + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return await self._post( + f"/conversations/{conversation_id}/items", + body=await async_maybe_transform({"items": items}, item_create_params.ItemCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, item_create_params.ItemCreateParams), + ), + cast_to=ConversationItemList, + ) + + async def retrieve( + self, + item_id: str, + *, + conversation_id: str, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ConversationItem: + """ + Get a single item from a conversation with the given IDs. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return cast( + ConversationItem, + await self._get( + f"/conversations/{conversation_id}/items/{item_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"include": include}, item_retrieve_params.ItemRetrieveParams), + ), + cast_to=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ), + ) + + def list( + self, + conversation_id: str, + *, + after: str | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + limit: int | NotGiven = NOT_GIVEN, + order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[ConversationItem, AsyncConversationCursorPage[ConversationItem]]: + """ + List all items for a conversation with the given ID. + + Args: + after: An item ID to list items after, used in pagination. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + return self._get_api_list( + f"/conversations/{conversation_id}/items", + page=AsyncConversationCursorPage[ConversationItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + item_list_params.ItemListParams, + ), + ), + model=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system + ) + + async def delete( + self, + item_id: str, + *, + conversation_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Conversation: + """ + Delete an item from a conversation with the given IDs. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not conversation_id: + raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") + if not item_id: + raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") + return await self._delete( + f"/conversations/{conversation_id}/items/{item_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Conversation, + ) + + +class ItemsWithRawResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.create = _legacy_response.to_raw_response_wrapper( + items.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + items.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + items.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + items.delete, + ) + + +class AsyncItemsWithRawResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.create = _legacy_response.async_to_raw_response_wrapper( + items.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + items.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + items.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + items.delete, + ) + + +class ItemsWithStreamingResponse: + def __init__(self, items: Items) -> None: + self._items = items + + self.create = to_streamed_response_wrapper( + items.create, + ) + self.retrieve = to_streamed_response_wrapper( + items.retrieve, + ) + self.list = to_streamed_response_wrapper( + items.list, + ) + self.delete = to_streamed_response_wrapper( + items.delete, + ) + + +class AsyncItemsWithStreamingResponse: + def __init__(self, items: AsyncItems) -> None: + self._items = items + + self.create = async_to_streamed_response_wrapper( + items.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + items.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + items.list, + ) + self.delete = async_to_streamed_response_wrapper( + items.delete, + ) diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py index a425a65c3e..9f3ef637ce 100644 --- a/src/openai/resources/responses/input_items.py +++ b/src/openai/resources/responses/input_items.py @@ -47,7 +47,6 @@ def list( response_id: str, *, after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, @@ -64,8 +63,6 @@ def list( Args: after: An item ID to list items after, used in pagination. - before: An item ID to list items before, used in pagination. - include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. @@ -98,7 +95,6 @@ def list( query=maybe_transform( { "after": after, - "before": before, "include": include, "limit": limit, "order": order, @@ -135,7 +131,6 @@ def list( response_id: str, *, after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, limit: int | NotGiven = NOT_GIVEN, order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, @@ -152,8 +147,6 @@ def list( Args: after: An item ID to list items after, used in pagination. - before: An item ID to list items before, used in pagination. - include: Additional fields to include in the response. See the `include` parameter for Response creation above for more information. @@ -186,7 +179,6 @@ def list( query=maybe_transform( { "after": after, - "before": before, "include": include, "limit": limit, "order": order, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 375f8b7e71..d0862f5d76 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -77,6 +77,7 @@ def create( self, *, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -127,6 +128,11 @@ def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + include: Specify additional output data to include in the model response. Currently supported values are: @@ -187,6 +193,7 @@ def create( previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). @@ -305,6 +312,7 @@ def create( *, stream: Literal[True], background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -361,6 +369,11 @@ def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + include: Specify additional output data to include in the model response. Currently supported values are: @@ -421,6 +434,7 @@ def create( previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). @@ -532,6 +546,7 @@ def create( *, stream: bool, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -588,6 +603,11 @@ def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + include: Specify additional output data to include in the model response. Currently supported values are: @@ -648,6 +668,7 @@ def create( previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). @@ -757,6 +778,7 @@ def create( self, *, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -794,6 +816,7 @@ def create( body=maybe_transform( { "background": background, + "conversation": conversation, "include": include, "input": input, "instructions": instructions, @@ -866,7 +889,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -1009,6 +1032,7 @@ def parse( *, text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1027,7 +1051,7 @@ def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -1065,6 +1089,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: body=maybe_transform( { "background": background, + "conversation": conversation, "include": include, "input": input, "instructions": instructions, @@ -1440,6 +1465,7 @@ async def create( self, *, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1490,6 +1516,11 @@ async def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + include: Specify additional output data to include in the model response. Currently supported values are: @@ -1550,6 +1581,7 @@ async def create( previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). @@ -1668,6 +1700,7 @@ async def create( *, stream: Literal[True], background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1724,6 +1757,11 @@ async def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + include: Specify additional output data to include in the model response. Currently supported values are: @@ -1784,6 +1822,7 @@ async def create( previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). @@ -1895,6 +1934,7 @@ async def create( *, stream: bool, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -1951,6 +1991,11 @@ async def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + include: Specify additional output data to include in the model response. Currently supported values are: @@ -2011,6 +2056,7 @@ async def create( previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). @@ -2120,6 +2166,7 @@ async def create( self, *, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -2157,6 +2204,7 @@ async def create( body=await async_maybe_transform( { "background": background, + "conversation": conversation, "include": include, "input": input, "instructions": instructions, @@ -2229,7 +2277,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -2261,7 +2309,7 @@ def stream( store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, @@ -2376,6 +2424,7 @@ async def parse( *, text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, background: Optional[bool] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, @@ -2394,7 +2443,7 @@ async def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2432,6 +2481,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: body=maybe_transform( { "background": background, + "conversation": conversation, "include": include, "input": input, "instructions": instructions, diff --git a/src/openai/types/conversations/__init__.py b/src/openai/types/conversations/__init__.py new file mode 100644 index 0000000000..538966db4f --- /dev/null +++ b/src/openai/types/conversations/__init__.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .message import Message as Message +from .lob_prob import LobProb as LobProb +from .conversation import Conversation as Conversation +from .text_content import TextContent as TextContent +from .top_log_prob import TopLogProb as TopLogProb +from .refusal_content import RefusalContent as RefusalContent +from .item_list_params import ItemListParams as ItemListParams +from .conversation_item import ConversationItem as ConversationItem +from .url_citation_body import URLCitationBody as URLCitationBody +from .file_citation_body import FileCitationBody as FileCitationBody +from .input_file_content import InputFileContent as InputFileContent +from .input_text_content import InputTextContent as InputTextContent +from .item_create_params import ItemCreateParams as ItemCreateParams +from .input_image_content import InputImageContent as InputImageContent +from .output_text_content import OutputTextContent as OutputTextContent +from .item_retrieve_params import ItemRetrieveParams as ItemRetrieveParams +from .summary_text_content import SummaryTextContent as SummaryTextContent +from .conversation_item_list import ConversationItemList as ConversationItemList +from .conversation_create_params import ConversationCreateParams as ConversationCreateParams +from .conversation_update_params import ConversationUpdateParams as ConversationUpdateParams +from .computer_screenshot_content import ComputerScreenshotContent as ComputerScreenshotContent +from .container_file_citation_body import ContainerFileCitationBody as ContainerFileCitationBody +from .conversation_deleted_resource import ConversationDeletedResource as ConversationDeletedResource diff --git a/src/openai/types/conversations/computer_screenshot_content.py b/src/openai/types/conversations/computer_screenshot_content.py new file mode 100644 index 0000000000..897b7ada0d --- /dev/null +++ b/src/openai/types/conversations/computer_screenshot_content.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ComputerScreenshotContent"] + + +class ComputerScreenshotContent(BaseModel): + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ diff --git a/src/openai/types/conversations/container_file_citation_body.py b/src/openai/types/conversations/container_file_citation_body.py new file mode 100644 index 0000000000..ea460df2e2 --- /dev/null +++ b/src/openai/types/conversations/container_file_citation_body.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ContainerFileCitationBody"] + + +class ContainerFileCitationBody(BaseModel): + container_id: str + """The ID of the container file.""" + + end_index: int + """The index of the last character of the container file citation in the message.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the container file citation in the message.""" + + type: Literal["container_file_citation"] + """The type of the container file citation. Always `container_file_citation`.""" diff --git a/src/openai/types/conversations/conversation.py b/src/openai/types/conversations/conversation.py new file mode 100644 index 0000000000..ed63d40355 --- /dev/null +++ b/src/openai/types/conversations/conversation.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Conversation"] + + +class Conversation(BaseModel): + id: str + """The unique ID of the conversation.""" + + created_at: int + """ + The time at which the conversation was created, measured in seconds since the + Unix epoch. + """ + + metadata: object + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters. + """ + + object: Literal["conversation"] + """The object type, which is always `conversation`.""" diff --git a/src/openai/types/conversations/conversation_create_params.py b/src/openai/types/conversations/conversation_create_params.py new file mode 100644 index 0000000000..7ad3f8ae2d --- /dev/null +++ b/src/openai/types/conversations/conversation_create_params.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import TypedDict + +from ..shared_params.metadata import Metadata +from ..responses.response_input_item_param import ResponseInputItemParam + +__all__ = ["ConversationCreateParams"] + + +class ConversationCreateParams(TypedDict, total=False): + items: Optional[Iterable[ResponseInputItemParam]] + """ + Initial items to include in the conversation context. You may add up to 20 items + at a time. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + Useful for storing additional information about the object in a structured + format. + """ diff --git a/src/openai/types/conversations/conversation_deleted_resource.py b/src/openai/types/conversations/conversation_deleted_resource.py new file mode 100644 index 0000000000..7abcb2448e --- /dev/null +++ b/src/openai/types/conversations/conversation_deleted_resource.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationDeletedResource"] + + +class ConversationDeletedResource(BaseModel): + id: str + + deleted: bool + + object: Literal["conversation.deleted"] diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py new file mode 100644 index 0000000000..a7cd355f36 --- /dev/null +++ b/src/openai/types/conversations/conversation_item.py @@ -0,0 +1,209 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from .message import Message +from ..._utils import PropertyInfo +from ..._models import BaseModel +from ..responses.response_reasoning_item import ResponseReasoningItem +from ..responses.response_custom_tool_call import ResponseCustomToolCall +from ..responses.response_computer_tool_call import ResponseComputerToolCall +from ..responses.response_function_web_search import ResponseFunctionWebSearch +from ..responses.response_file_search_tool_call import ResponseFileSearchToolCall +from ..responses.response_custom_tool_call_output import ResponseCustomToolCallOutput +from ..responses.response_function_tool_call_item import ResponseFunctionToolCallItem +from ..responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from ..responses.response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from ..responses.response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem + +__all__ = [ + "ConversationItem", + "ImageGenerationCall", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallOutput", + "McpListTools", + "McpListToolsTool", + "McpApprovalRequest", + "McpApprovalResponse", + "McpCall", +] + + +class ImageGenerationCall(BaseModel): + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + +class LocalShellCallAction(BaseModel): + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCall(BaseModel): + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + +class LocalShellCallOutput(BaseModel): + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpListToolsTool(BaseModel): + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListTools(BaseModel): + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequest(BaseModel): + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + +class McpApprovalResponse(BaseModel): + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCall(BaseModel): + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + error: Optional[str] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + +ConversationItem: TypeAlias = Annotated[ + Union[ + Message, + ResponseFunctionToolCallItem, + ResponseFunctionToolCallOutputItem, + ResponseFileSearchToolCall, + ResponseFunctionWebSearch, + ImageGenerationCall, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseReasoningItem, + ResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + ResponseCustomToolCall, + ResponseCustomToolCallOutput, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/conversations/conversation_item_list.py b/src/openai/types/conversations/conversation_item_list.py new file mode 100644 index 0000000000..20091102cb --- /dev/null +++ b/src/openai/types/conversations/conversation_item_list.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemList"] + + +class ConversationItemList(BaseModel): + data: List[ConversationItem] + """A list of conversation items.""" + + first_id: str + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: str + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/src/openai/types/conversations/conversation_update_params.py b/src/openai/types/conversations/conversation_update_params.py new file mode 100644 index 0000000000..f2aa42d833 --- /dev/null +++ b/src/openai/types/conversations/conversation_update_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, TypedDict + +__all__ = ["ConversationUpdateParams"] + + +class ConversationUpdateParams(TypedDict, total=False): + metadata: Required[Dict[str, str]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters. + """ diff --git a/src/openai/types/conversations/file_citation_body.py b/src/openai/types/conversations/file_citation_body.py new file mode 100644 index 0000000000..ea90ae381d --- /dev/null +++ b/src/openai/types/conversations/file_citation_body.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FileCitationBody"] + + +class FileCitationBody(BaseModel): + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" diff --git a/src/openai/types/conversations/input_file_content.py b/src/openai/types/conversations/input_file_content.py new file mode 100644 index 0000000000..6aef7a89d9 --- /dev/null +++ b/src/openai/types/conversations/input_file_content.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputFileContent"] + + +class InputFileContent(BaseModel): + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" diff --git a/src/openai/types/conversations/input_image_content.py b/src/openai/types/conversations/input_image_content.py new file mode 100644 index 0000000000..f2587e0adc --- /dev/null +++ b/src/openai/types/conversations/input_image_content.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputImageContent"] + + +class InputImageContent(BaseModel): + detail: Literal["low", "high", "auto"] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" diff --git a/src/openai/types/conversations/input_text_content.py b/src/openai/types/conversations/input_text_content.py new file mode 100644 index 0000000000..5e2daebdc5 --- /dev/null +++ b/src/openai/types/conversations/input_text_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputTextContent"] + + +class InputTextContent(BaseModel): + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" diff --git a/src/openai/types/conversations/item_create_params.py b/src/openai/types/conversations/item_create_params.py new file mode 100644 index 0000000000..9158b7167f --- /dev/null +++ b/src/openai/types/conversations/item_create_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Iterable +from typing_extensions import Required, TypedDict + +from ..responses.response_includable import ResponseIncludable +from ..responses.response_input_item_param import ResponseInputItemParam + +__all__ = ["ItemCreateParams"] + + +class ItemCreateParams(TypedDict, total=False): + items: Required[Iterable[ResponseInputItemParam]] + """The items to add to the conversation. You may add up to 20 items at a time.""" + + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for + [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) + for more information. + """ diff --git a/src/openai/types/conversations/item_list_params.py b/src/openai/types/conversations/item_list_params.py new file mode 100644 index 0000000000..34bf43c559 --- /dev/null +++ b/src/openai/types/conversations/item_list_params.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +from ..responses.response_includable import ResponseIncludable + +__all__ = ["ItemListParams"] + + +class ItemListParams(TypedDict, total=False): + after: str + """An item ID to list items after, used in pagination.""" + + include: List[ResponseIncludable] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + """ diff --git a/src/openai/types/conversations/item_retrieve_params.py b/src/openai/types/conversations/item_retrieve_params.py new file mode 100644 index 0000000000..8c5db1e533 --- /dev/null +++ b/src/openai/types/conversations/item_retrieve_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, TypedDict + +from ..responses.response_includable import ResponseIncludable + +__all__ = ["ItemRetrieveParams"] + + +class ItemRetrieveParams(TypedDict, total=False): + conversation_id: Required[str] + + include: List[ResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for + [listing Conversation items above](https://platform.openai.com/docs/api-reference/conversations/list-items#conversations_list_items-include) + for more information. + """ diff --git a/src/openai/types/conversations/lob_prob.py b/src/openai/types/conversations/lob_prob.py new file mode 100644 index 0000000000..f7dcd62a5e --- /dev/null +++ b/src/openai/types/conversations/lob_prob.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel +from .top_log_prob import TopLogProb + +__all__ = ["LobProb"] + + +class LobProb(BaseModel): + token: str + + bytes: List[int] + + logprob: float + + top_logprobs: List[TopLogProb] diff --git a/src/openai/types/conversations/message.py b/src/openai/types/conversations/message.py new file mode 100644 index 0000000000..a070cf2869 --- /dev/null +++ b/src/openai/types/conversations/message.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .text_content import TextContent +from .refusal_content import RefusalContent +from .input_file_content import InputFileContent +from .input_text_content import InputTextContent +from .input_image_content import InputImageContent +from .output_text_content import OutputTextContent +from .summary_text_content import SummaryTextContent +from .computer_screenshot_content import ComputerScreenshotContent + +__all__ = ["Message", "Content"] + +Content: TypeAlias = Annotated[ + Union[ + InputTextContent, + OutputTextContent, + TextContent, + SummaryTextContent, + RefusalContent, + InputImageContent, + ComputerScreenshotContent, + InputFileContent, + ], + PropertyInfo(discriminator="type"), +] + + +class Message(BaseModel): + id: str + """The unique ID of the message.""" + + content: List[Content] + """The content of the message""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role of the message. + + One of `unknown`, `user`, `assistant`, `system`, `critic`, `discriminator`, + `developer`, or `tool`. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message. Always set to `message`.""" diff --git a/src/openai/types/conversations/output_text_content.py b/src/openai/types/conversations/output_text_content.py new file mode 100644 index 0000000000..2ffee76526 --- /dev/null +++ b/src/openai/types/conversations/output_text_content.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .lob_prob import LobProb +from ..._models import BaseModel +from .url_citation_body import URLCitationBody +from .file_citation_body import FileCitationBody +from .container_file_citation_body import ContainerFileCitationBody + +__all__ = ["OutputTextContent", "Annotation"] + +Annotation: TypeAlias = Annotated[ + Union[FileCitationBody, URLCitationBody, ContainerFileCitationBody], PropertyInfo(discriminator="type") +] + + +class OutputTextContent(BaseModel): + annotations: List[Annotation] + """The annotations of the text output.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + logprobs: Optional[List[LobProb]] = None diff --git a/src/openai/types/conversations/refusal_content.py b/src/openai/types/conversations/refusal_content.py new file mode 100644 index 0000000000..3c8bd5e35f --- /dev/null +++ b/src/openai/types/conversations/refusal_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RefusalContent"] + + +class RefusalContent(BaseModel): + refusal: str + """The refusal explanation from the model.""" + + type: Literal["refusal"] + """The type of the refusal. Always `refusal`.""" diff --git a/src/openai/types/conversations/summary_text_content.py b/src/openai/types/conversations/summary_text_content.py new file mode 100644 index 0000000000..047769ed67 --- /dev/null +++ b/src/openai/types/conversations/summary_text_content.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SummaryTextContent"] + + +class SummaryTextContent(BaseModel): + text: str + + type: Literal["summary_text"] diff --git a/src/openai/types/conversations/text_content.py b/src/openai/types/conversations/text_content.py new file mode 100644 index 0000000000..f1ae079597 --- /dev/null +++ b/src/openai/types/conversations/text_content.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TextContent"] + + +class TextContent(BaseModel): + text: str + + type: Literal["text"] diff --git a/src/openai/types/conversations/top_log_prob.py b/src/openai/types/conversations/top_log_prob.py new file mode 100644 index 0000000000..fafca756ae --- /dev/null +++ b/src/openai/types/conversations/top_log_prob.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["TopLogProb"] + + +class TopLogProb(BaseModel): + token: str + + bytes: List[int] + + logprob: float diff --git a/src/openai/types/conversations/url_citation_body.py b/src/openai/types/conversations/url_citation_body.py new file mode 100644 index 0000000000..1becb44bc0 --- /dev/null +++ b/src/openai/types/conversations/url_citation_body.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["URLCitationBody"] + + +class URLCitationBody(BaseModel): + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index bb39d1d3e5..efcab9adb8 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -23,10 +23,10 @@ "InputMessages", "InputMessagesTemplate", "InputMessagesTemplateTemplate", - "InputMessagesTemplateTemplateMessage", - "InputMessagesTemplateTemplateMessageContent", - "InputMessagesTemplateTemplateMessageContentOutputText", - "InputMessagesTemplateTemplateMessageContentInputImage", + "InputMessagesTemplateTemplateEvalItem", + "InputMessagesTemplateTemplateEvalItemContent", + "InputMessagesTemplateTemplateEvalItemContentOutputText", + "InputMessagesTemplateTemplateEvalItemContentInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -87,7 +87,7 @@ class SourceStoredCompletions(BaseModel): ] -class InputMessagesTemplateTemplateMessageContentOutputText(BaseModel): +class InputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): text: str """The text output from the model.""" @@ -95,7 +95,7 @@ class InputMessagesTemplateTemplateMessageContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -class InputMessagesTemplateTemplateMessageContentInputImage(BaseModel): +class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): image_url: str """The URL of the image input.""" @@ -109,17 +109,17 @@ class InputMessagesTemplateTemplateMessageContentInputImage(BaseModel): """ -InputMessagesTemplateTemplateMessageContent: TypeAlias = Union[ +InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, - InputMessagesTemplateTemplateMessageContentOutputText, - InputMessagesTemplateTemplateMessageContentInputImage, + InputMessagesTemplateTemplateEvalItemContentOutputText, + InputMessagesTemplateTemplateEvalItemContentInputImage, List[object], ] -class InputMessagesTemplateTemplateMessage(BaseModel): - content: InputMessagesTemplateTemplateMessageContent +class InputMessagesTemplateTemplateEvalItem(BaseModel): + content: InputMessagesTemplateTemplateEvalItemContent """Inputs to the model - can contain template strings.""" role: Literal["user", "assistant", "system", "developer"] @@ -132,9 +132,7 @@ class InputMessagesTemplateTemplateMessage(BaseModel): """The type of the message input. Always `message`.""" -InputMessagesTemplateTemplate: TypeAlias = Annotated[ - Union[EasyInputMessage, InputMessagesTemplateTemplateMessage], PropertyInfo(discriminator="type") -] +InputMessagesTemplateTemplate: TypeAlias = Union[EasyInputMessage, InputMessagesTemplateTemplateEvalItem] class InputMessagesTemplate(BaseModel): diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 7c71ecbe88..effa658452 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -23,10 +23,10 @@ "InputMessages", "InputMessagesTemplate", "InputMessagesTemplateTemplate", - "InputMessagesTemplateTemplateMessage", - "InputMessagesTemplateTemplateMessageContent", - "InputMessagesTemplateTemplateMessageContentOutputText", - "InputMessagesTemplateTemplateMessageContentInputImage", + "InputMessagesTemplateTemplateEvalItem", + "InputMessagesTemplateTemplateEvalItemContent", + "InputMessagesTemplateTemplateEvalItemContentOutputText", + "InputMessagesTemplateTemplateEvalItemContentInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -85,7 +85,7 @@ class SourceStoredCompletions(TypedDict, total=False): Source: TypeAlias = Union[SourceFileContent, SourceFileID, SourceStoredCompletions] -class InputMessagesTemplateTemplateMessageContentOutputText(TypedDict, total=False): +class InputMessagesTemplateTemplateEvalItemContentOutputText(TypedDict, total=False): text: Required[str] """The text output from the model.""" @@ -93,7 +93,7 @@ class InputMessagesTemplateTemplateMessageContentOutputText(TypedDict, total=Fal """The type of the output text. Always `output_text`.""" -class InputMessagesTemplateTemplateMessageContentInputImage(TypedDict, total=False): +class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=False): image_url: Required[str] """The URL of the image input.""" @@ -107,17 +107,17 @@ class InputMessagesTemplateTemplateMessageContentInputImage(TypedDict, total=Fal """ -InputMessagesTemplateTemplateMessageContent: TypeAlias = Union[ +InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, - InputMessagesTemplateTemplateMessageContentOutputText, - InputMessagesTemplateTemplateMessageContentInputImage, + InputMessagesTemplateTemplateEvalItemContentOutputText, + InputMessagesTemplateTemplateEvalItemContentInputImage, Iterable[object], ] -class InputMessagesTemplateTemplateMessage(TypedDict, total=False): - content: Required[InputMessagesTemplateTemplateMessageContent] +class InputMessagesTemplateTemplateEvalItem(TypedDict, total=False): + content: Required[InputMessagesTemplateTemplateEvalItemContent] """Inputs to the model - can contain template strings.""" role: Required[Literal["user", "assistant", "system", "developer"]] @@ -130,7 +130,7 @@ class InputMessagesTemplateTemplateMessage(TypedDict, total=False): """The type of the message input. Always `message`.""" -InputMessagesTemplateTemplate: TypeAlias = Union[EasyInputMessageParam, InputMessagesTemplateTemplateMessage] +InputMessagesTemplateTemplate: TypeAlias = Union[EasyInputMessageParam, InputMessagesTemplateTemplateEvalItem] class InputMessagesTemplate(TypedDict, total=False): diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 74d8688081..7c574ed315 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -79,6 +79,7 @@ from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam as ToolChoiceFunctionParam from .response_computer_tool_call import ResponseComputerToolCall as ResponseComputerToolCall +from .response_conversation_param import ResponseConversationParam as ResponseConversationParam from .response_format_text_config import ResponseFormatTextConfig as ResponseFormatTextConfig from .response_function_tool_call import ResponseFunctionToolCall as ResponseFunctionToolCall from .response_input_message_item import ResponseInputMessageItem as ResponseInputMessageItem diff --git a/src/openai/types/responses/input_item_list_params.py b/src/openai/types/responses/input_item_list_params.py index 6a18d920cb..44a8dc5de3 100644 --- a/src/openai/types/responses/input_item_list_params.py +++ b/src/openai/types/responses/input_item_list_params.py @@ -14,9 +14,6 @@ class InputItemListParams(TypedDict, total=False): after: str """An item ID to list items after, used in pagination.""" - before: str - """An item ID to list items before, used in pagination.""" - include: List[ResponseIncludable] """Additional fields to include in the response. diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 49f60bbc5c..ce9effd75e 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -22,7 +22,7 @@ from .tool_choice_function import ToolChoiceFunction from ..shared.responses_model import ResponsesModel -__all__ = ["Response", "IncompleteDetails", "ToolChoice"] +__all__ = ["Response", "IncompleteDetails", "ToolChoice", "Conversation"] class IncompleteDetails(BaseModel): @@ -35,6 +35,11 @@ class IncompleteDetails(BaseModel): ] +class Conversation(BaseModel): + id: str + """The unique ID of the conversation.""" + + class Response(BaseModel): id: str """Unique identifier for this Response.""" @@ -141,6 +146,13 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/background). """ + conversation: Optional[Conversation] = None + """The conversation that this response belongs to. + + Input items and output items from this response are automatically added to this + conversation. + """ + max_output_tokens: Optional[int] = None """ An upper bound for the number of tokens that can be generated for a response, @@ -161,6 +173,7 @@ class Response(BaseModel): Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. """ prompt: Optional[ResponsePrompt] = None diff --git a/src/openai/types/responses/response_conversation_param.py b/src/openai/types/responses/response_conversation_param.py new file mode 100644 index 0000000000..067bdc7a31 --- /dev/null +++ b/src/openai/types/responses/response_conversation_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ResponseConversationParam"] + + +class ResponseConversationParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the conversation.""" diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 0cd761fcf0..5129b8b771 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -18,10 +18,12 @@ from .tool_choice_allowed_param import ToolChoiceAllowedParam from .response_text_config_param import ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam +from .response_conversation_param import ResponseConversationParam from ..shared_params.responses_model import ResponsesModel __all__ = [ "ResponseCreateParamsBase", + "Conversation", "StreamOptions", "ToolChoice", "ResponseCreateParamsNonStreaming", @@ -36,6 +38,14 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/background). """ + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + include: Optional[List[ResponseIncludable]] """Specify additional output data to include in the model response. @@ -118,6 +128,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. """ prompt: Optional[ResponsePromptParam] @@ -253,6 +264,9 @@ class ResponseCreateParamsBase(TypedDict, total=False): """ +Conversation: TypeAlias = Union[str, ResponseConversationParam] + + class StreamOptions(TypedDict, total=False): include_obfuscation: bool """When true, stream obfuscation will be enabled. diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 455ba01666..d46f8cb0be 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -15,7 +15,7 @@ "Tool", "Mcp", "McpAllowedTools", - "McpAllowedToolsMcpAllowedToolsFilter", + "McpAllowedToolsMcpToolFilter", "McpRequireApproval", "McpRequireApprovalMcpToolApprovalFilter", "McpRequireApprovalMcpToolApprovalFilterAlways", @@ -29,30 +29,54 @@ ] -class McpAllowedToolsMcpAllowedToolsFilter(BaseModel): +class McpAllowedToolsMcpToolFilter(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + tool_names: Optional[List[str]] = None """List of allowed tool names.""" -McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpAllowedToolsFilter, None] +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter, None] class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + tool_names: Optional[List[str]] = None - """List of tools that require approval.""" + """List of allowed tool names.""" class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + tool_names: Optional[List[str]] = None - """List of tools that do not require approval.""" + """List of allowed tool names.""" class McpRequireApprovalMcpToolApprovalFilter(BaseModel): always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None - """A list of tools that always require approval.""" + """A filter object to specify which tools are allowed.""" never: Optional[McpRequireApprovalMcpToolApprovalFilterNever] = None - """A list of tools that never require approval.""" + """A filter object to specify which tools are allowed.""" McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] @@ -62,15 +86,49 @@ class Mcp(BaseModel): server_label: str """A label for this MCP server, used to identify it in tool calls.""" - server_url: str - """The URL for the MCP server.""" - type: Literal["mcp"] """The type of the MCP tool. Always `mcp`.""" allowed_tools: Optional[McpAllowedTools] = None """List of allowed tool names or a filter object.""" + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + headers: Optional[Dict[str, str]] = None """Optional HTTP headers to send to the MCP server. @@ -83,6 +141,12 @@ class Mcp(BaseModel): server_description: Optional[str] = None """Optional description of the MCP server, used to provide more context.""" + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url` or `connector_id` must be provided. + """ + class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): type: Literal["auto"] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index f91e758559..9dde42e294 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -16,7 +16,7 @@ "ToolParam", "Mcp", "McpAllowedTools", - "McpAllowedToolsMcpAllowedToolsFilter", + "McpAllowedToolsMcpToolFilter", "McpRequireApproval", "McpRequireApprovalMcpToolApprovalFilter", "McpRequireApprovalMcpToolApprovalFilterAlways", @@ -30,30 +30,54 @@ ] -class McpAllowedToolsMcpAllowedToolsFilter(TypedDict, total=False): +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + tool_names: List[str] """List of allowed tool names.""" -McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpAllowedToolsFilter] +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter] class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + tool_names: List[str] - """List of tools that require approval.""" + """List of allowed tool names.""" class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + tool_names: List[str] - """List of tools that do not require approval.""" + """List of allowed tool names.""" class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): always: McpRequireApprovalMcpToolApprovalFilterAlways - """A list of tools that always require approval.""" + """A filter object to specify which tools are allowed.""" never: McpRequireApprovalMcpToolApprovalFilterNever - """A list of tools that never require approval.""" + """A filter object to specify which tools are allowed.""" McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] @@ -63,15 +87,47 @@ class Mcp(TypedDict, total=False): server_label: Required[str] """A label for this MCP server, used to identify it in tool calls.""" - server_url: Required[str] - """The URL for the MCP server.""" - type: Required[Literal["mcp"]] """The type of the MCP tool. Always `mcp`.""" allowed_tools: Optional[McpAllowedTools] """List of allowed tool names or a filter object.""" + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + headers: Optional[Dict[str, str]] """Optional HTTP headers to send to the MCP server. @@ -84,6 +140,12 @@ class Mcp(TypedDict, total=False): server_description: str """Optional description of the MCP server, used to provide more context.""" + server_url: str + """The URL for the MCP server. + + One of `server_url` or `connector_id` must be provided. + """ + class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): type: Required[Literal["auto"]] diff --git a/tests/api_resources/conversations/__init__.py b/tests/api_resources/conversations/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/conversations/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/conversations/test_items.py b/tests/api_resources/conversations/test_items.py new file mode 100644 index 0000000000..c308160543 --- /dev/null +++ b/tests/api_resources/conversations/test_items.py @@ -0,0 +1,491 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.conversations import ( + Conversation, + ConversationItem, + ConversationItemList, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestItems: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + item = client.conversations.items.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) + assert_matches_type(ConversationItemList, item, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + item = client.conversations.items.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + "type": "message", + } + ], + include=["code_interpreter_call.outputs"], + ) + assert_matches_type(ConversationItemList, item, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.conversations.items.with_raw_response.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(ConversationItemList, item, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.conversations.items.with_streaming_response.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(ConversationItemList, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + client.conversations.items.with_raw_response.create( + conversation_id="", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + item = client.conversations.items.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + ) + assert_matches_type(ConversationItem, item, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: + item = client.conversations.items.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + include=["code_interpreter_call.outputs"], + ) + assert_matches_type(ConversationItem, item, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.conversations.items.with_raw_response.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(ConversationItem, item, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.conversations.items.with_streaming_response.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(ConversationItem, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + client.conversations.items.with_raw_response.retrieve( + item_id="msg_abc", + conversation_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"): + client.conversations.items.with_raw_response.retrieve( + item_id="", + conversation_id="conv_123", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + item = client.conversations.items.list( + conversation_id="conv_123", + ) + assert_matches_type(SyncConversationCursorPage[ConversationItem], item, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + item = client.conversations.items.list( + conversation_id="conv_123", + after="after", + include=["code_interpreter_call.outputs"], + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[ConversationItem], item, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.conversations.items.with_raw_response.list( + conversation_id="conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(SyncConversationCursorPage[ConversationItem], item, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.conversations.items.with_streaming_response.list( + conversation_id="conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(SyncConversationCursorPage[ConversationItem], item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + client.conversations.items.with_raw_response.list( + conversation_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + item = client.conversations.items.delete( + item_id="msg_abc", + conversation_id="conv_123", + ) + assert_matches_type(Conversation, item, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.conversations.items.with_raw_response.delete( + item_id="msg_abc", + conversation_id="conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(Conversation, item, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.conversations.items.with_streaming_response.delete( + item_id="msg_abc", + conversation_id="conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(Conversation, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + client.conversations.items.with_raw_response.delete( + item_id="msg_abc", + conversation_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"): + client.conversations.items.with_raw_response.delete( + item_id="", + conversation_id="conv_123", + ) + + +class TestAsyncItems: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + item = await async_client.conversations.items.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) + assert_matches_type(ConversationItemList, item, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + item = await async_client.conversations.items.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + "type": "message", + } + ], + include=["code_interpreter_call.outputs"], + ) + assert_matches_type(ConversationItemList, item, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.items.with_raw_response.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(ConversationItemList, item, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.items.with_streaming_response.create( + conversation_id="conv_123", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(ConversationItemList, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + await async_client.conversations.items.with_raw_response.create( + conversation_id="", + items=[ + { + "content": "string", + "role": "user", + } + ], + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + item = await async_client.conversations.items.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + ) + assert_matches_type(ConversationItem, item, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: + item = await async_client.conversations.items.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + include=["code_interpreter_call.outputs"], + ) + assert_matches_type(ConversationItem, item, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.items.with_raw_response.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(ConversationItem, item, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.items.with_streaming_response.retrieve( + item_id="msg_abc", + conversation_id="conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(ConversationItem, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + await async_client.conversations.items.with_raw_response.retrieve( + item_id="msg_abc", + conversation_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"): + await async_client.conversations.items.with_raw_response.retrieve( + item_id="", + conversation_id="conv_123", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + item = await async_client.conversations.items.list( + conversation_id="conv_123", + ) + assert_matches_type(AsyncConversationCursorPage[ConversationItem], item, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + item = await async_client.conversations.items.list( + conversation_id="conv_123", + after="after", + include=["code_interpreter_call.outputs"], + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[ConversationItem], item, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.items.with_raw_response.list( + conversation_id="conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(AsyncConversationCursorPage[ConversationItem], item, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.items.with_streaming_response.list( + conversation_id="conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ConversationItem], item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + await async_client.conversations.items.with_raw_response.list( + conversation_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + item = await async_client.conversations.items.delete( + item_id="msg_abc", + conversation_id="conv_123", + ) + assert_matches_type(Conversation, item, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.items.with_raw_response.delete( + item_id="msg_abc", + conversation_id="conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(Conversation, item, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.items.with_streaming_response.delete( + item_id="msg_abc", + conversation_id="conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(Conversation, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + await async_client.conversations.items.with_raw_response.delete( + item_id="msg_abc", + conversation_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `item_id` but received ''"): + await async_client.conversations.items.with_raw_response.delete( + item_id="", + conversation_id="conv_123", + ) diff --git a/tests/api_resources/responses/test_input_items.py b/tests/api_resources/responses/test_input_items.py index e8e3893bad..eda20c9a0b 100644 --- a/tests/api_resources/responses/test_input_items.py +++ b/tests/api_resources/responses/test_input_items.py @@ -30,7 +30,6 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: input_item = client.responses.input_items.list( response_id="response_id", after="after", - before="before", include=["code_interpreter_call.outputs"], limit=0, order="asc", @@ -86,7 +85,6 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N input_item = await async_client.responses.input_items.list( response_id="response_id", after="after", - before="before", include=["code_interpreter_call.outputs"], limit=0, order="asc", diff --git a/tests/api_resources/test_conversations.py b/tests/api_resources/test_conversations.py new file mode 100644 index 0000000000..d21e685a04 --- /dev/null +++ b/tests/api_resources/test_conversations.py @@ -0,0 +1,341 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.conversations import ( + Conversation, + ConversationDeletedResource, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestConversations: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + conversation = client.conversations.create() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + conversation = client.conversations.create( + items=[ + { + "content": "string", + "role": "user", + "type": "message", + } + ], + metadata={"foo": "string"}, + ) + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.conversations.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.conversations.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + conversation = client.conversations.retrieve( + "conv_123", + ) + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.conversations.with_raw_response.retrieve( + "conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.conversations.with_streaming_response.retrieve( + "conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + client.conversations.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + conversation = client.conversations.update( + conversation_id="conv_123", + metadata={"foo": "string"}, + ) + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.conversations.with_raw_response.update( + conversation_id="conv_123", + metadata={"foo": "string"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.conversations.with_streaming_response.update( + conversation_id="conv_123", + metadata={"foo": "string"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + client.conversations.with_raw_response.update( + conversation_id="", + metadata={"foo": "string"}, + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + conversation = client.conversations.delete( + "conv_123", + ) + assert_matches_type(ConversationDeletedResource, conversation, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.conversations.with_raw_response.delete( + "conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationDeletedResource, conversation, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.conversations.with_streaming_response.delete( + "conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = response.parse() + assert_matches_type(ConversationDeletedResource, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + client.conversations.with_raw_response.delete( + "", + ) + + +class TestAsyncConversations: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + conversation = await async_client.conversations.create() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + conversation = await async_client.conversations.create( + items=[ + { + "content": "string", + "role": "user", + "type": "message", + } + ], + metadata={"foo": "string"}, + ) + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + conversation = await async_client.conversations.retrieve( + "conv_123", + ) + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.with_raw_response.retrieve( + "conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.with_streaming_response.retrieve( + "conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + await async_client.conversations.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + conversation = await async_client.conversations.update( + conversation_id="conv_123", + metadata={"foo": "string"}, + ) + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.with_raw_response.update( + conversation_id="conv_123", + metadata={"foo": "string"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.with_streaming_response.update( + conversation_id="conv_123", + metadata={"foo": "string"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(Conversation, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + await async_client.conversations.with_raw_response.update( + conversation_id="", + metadata={"foo": "string"}, + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + conversation = await async_client.conversations.delete( + "conv_123", + ) + assert_matches_type(ConversationDeletedResource, conversation, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.conversations.with_raw_response.delete( + "conv_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + conversation = response.parse() + assert_matches_type(ConversationDeletedResource, conversation, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.conversations.with_streaming_response.delete( + "conv_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + conversation = await response.parse() + assert_matches_type(ConversationDeletedResource, conversation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `conversation_id` but received ''"): + await async_client.conversations.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 310800b87e..0cc20e926b 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -29,6 +29,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: response = client.responses.create( background=True, + conversation="string", include=["code_interpreter_call.outputs"], input="string", instructions="instructions", @@ -108,6 +109,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: response_stream = client.responses.create( stream=True, background=True, + conversation="string", include=["code_interpreter_call.outputs"], input="string", instructions="instructions", @@ -380,6 +382,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.create( background=True, + conversation="string", include=["code_interpreter_call.outputs"], input="string", instructions="instructions", @@ -459,6 +462,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn response_stream = await async_client.responses.create( stream=True, background=True, + conversation="string", include=["code_interpreter_call.outputs"], input="string", instructions="instructions", From 9fd9df51bb12956598d6e12b50a3330aa0e56272 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 25 Aug 2025 22:24:33 +0000 Subject: [PATCH 073/408] chore(internal): change ci workflow machines --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e56aae09a..4c617a6f19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: permissions: contents: read id-token: write - runs-on: depot-ubuntu-24.04 + runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@v4 From 7325cdbbaf88078d00fefdb830f5040272b35dda Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 13:56:43 +0000 Subject: [PATCH 074/408] chore(internal): codegen related update --- requirements-dev.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index e619cb6b64..e8bea53014 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -70,7 +70,7 @@ filelock==3.12.4 frozenlist==1.7.0 # via aiohttp # via aiosignal -griffe==1.12.1 +griffe==1.13.0 h11==0.16.0 # via httpcore httpcore==1.0.9 From 3f21bcd0b993641402e28d21621b794db0b34cc2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:02:14 +0000 Subject: [PATCH 075/408] fix: avoid newer type syntax --- src/openai/_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index d84d51d913..50eb0af751 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -329,7 +329,7 @@ def model_dump( exclude_none=exclude_none, ) - return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped + return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped @override def model_dump_json( From af5f9c4e9d26777364154c2961dce7a047a2b42d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 20:42:47 +0000 Subject: [PATCH 076/408] feat(api): add web search filters --- .stats.yml | 4 +- .../resources/conversations/conversations.py | 4 +- src/openai/resources/conversations/items.py | 4 ++ src/openai/resources/responses/responses.py | 12 ++++ .../types/conversations/item_list_params.py | 2 + .../types/responses/response_create_params.py | 2 + .../responses/response_function_web_search.py | 15 ++++- .../response_function_web_search_param.py | 22 ++++++- src/openai/types/responses/tool.py | 63 ++++++++++++++++++- src/openai/types/responses/tool_param.py | 61 +++++++++++++++++- 10 files changed, 178 insertions(+), 11 deletions(-) diff --git a/.stats.yml b/.stats.yml index f2d5304a5b..5ad90ac5ab 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 119 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-ddbdf9343316047e8a773c54fb24e4a8d225955e202a1888fde6f9c8898ebf98.yml -openapi_spec_hash: 9802f6dd381558466c897f6e387e06ca +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-8517ffa1004e31ca2523d617629e64be6fe4f13403ddfd9db5b3be002656cbde.yml +openapi_spec_hash: b64dd8c8b23082a7aa2a3e5c5fffd8bd config_hash: fe0ea26680ac2075a6cd66416aefe7db diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py index 13bc1fb1ce..802620e6ad 100644 --- a/src/openai/resources/conversations/conversations.py +++ b/src/openai/resources/conversations/conversations.py @@ -67,7 +67,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Conversation: """ - Create a conversation with the given ID. + Create a conversation. Args: items: Initial items to include in the conversation context. You may add up to 20 items @@ -244,7 +244,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> Conversation: """ - Create a conversation with the given ID. + Create a conversation. Args: items: Initial items to include in the conversation context. You may add up to 20 items diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py index 1e696a79ed..01811f956b 100644 --- a/src/openai/resources/conversations/items.py +++ b/src/openai/resources/conversations/items.py @@ -163,6 +163,8 @@ def list( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer @@ -391,6 +393,8 @@ def list( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index d0862f5d76..062fd491f2 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -136,6 +136,8 @@ def create( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer @@ -377,6 +379,8 @@ def create( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer @@ -611,6 +615,8 @@ def create( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer @@ -1524,6 +1530,8 @@ async def create( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer @@ -1765,6 +1773,8 @@ async def create( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer @@ -1999,6 +2009,8 @@ async def create( include: Specify additional output data to include in the model response. Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer diff --git a/src/openai/types/conversations/item_list_params.py b/src/openai/types/conversations/item_list_params.py index 34bf43c559..a4dd61f399 100644 --- a/src/openai/types/conversations/item_list_params.py +++ b/src/openai/types/conversations/item_list_params.py @@ -19,6 +19,8 @@ class ItemListParams(TypedDict, total=False): Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 5129b8b771..ff28c05816 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -51,6 +51,8 @@ class ResponseCreateParamsBase(TypedDict, total=False): Currently supported values are: + - `web_search_call.action.sources`: Include the sources of the web search tool + call. - `code_interpreter_call.outputs`: Includes the outputs of python code execution in code interpreter tool call items. - `computer_call_output.output.image_url`: Include image urls from the computer diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index a3252956e9..f3e80e6a8f 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -1,12 +1,20 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union +from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel -__all__ = ["ResponseFunctionWebSearch", "Action", "ActionSearch", "ActionOpenPage", "ActionFind"] +__all__ = ["ResponseFunctionWebSearch", "Action", "ActionSearch", "ActionSearchSource", "ActionOpenPage", "ActionFind"] + + +class ActionSearchSource(BaseModel): + type: Literal["url"] + """The type of source. Always `url`.""" + + url: str + """The URL of the source.""" class ActionSearch(BaseModel): @@ -16,6 +24,9 @@ class ActionSearch(BaseModel): type: Literal["search"] """The action type.""" + sources: Optional[List[ActionSearchSource]] = None + """The sources used in the search.""" + class ActionOpenPage(BaseModel): type: Literal["open_page"] diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 4a06132cf4..fc019d3eb7 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -2,10 +2,25 @@ from __future__ import annotations -from typing import Union +from typing import Union, Iterable from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["ResponseFunctionWebSearchParam", "Action", "ActionSearch", "ActionOpenPage", "ActionFind"] +__all__ = [ + "ResponseFunctionWebSearchParam", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFind", +] + + +class ActionSearchSource(TypedDict, total=False): + type: Required[Literal["url"]] + """The type of source. Always `url`.""" + + url: Required[str] + """The URL of the source.""" class ActionSearch(TypedDict, total=False): @@ -15,6 +30,9 @@ class ActionSearch(TypedDict, total=False): type: Required[Literal["search"]] """The action type.""" + sources: Iterable[ActionSearchSource] + """The sources used in the search.""" + class ActionOpenPage(TypedDict, total=False): type: Required[Literal["open_page"]] diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index d46f8cb0be..0fe7133804 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -3,16 +3,19 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias +from . import web_search_tool from ..._utils import PropertyInfo from ..._models import BaseModel from .custom_tool import CustomTool from .computer_tool import ComputerTool from .function_tool import FunctionTool -from .web_search_tool import WebSearchTool from .file_search_tool import FileSearchTool __all__ = [ "Tool", + "WebSearchTool", + "WebSearchToolFilters", + "WebSearchToolUserLocation", "Mcp", "McpAllowedTools", "McpAllowedToolsMcpToolFilter", @@ -29,6 +32,61 @@ ] +class WebSearchToolFilters(BaseModel): + allowed_domains: Optional[List[str]] = None + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class WebSearchToolUserLocation(BaseModel): + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Optional[Literal["approximate"]] = None + """The type of location approximation. Always `approximate`.""" + + +class WebSearchTool(BaseModel): + type: Literal["web_search", "web_search_2025_08_26"] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + filters: Optional[WebSearchToolFilters] = None + """Filters for the search.""" + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[WebSearchToolUserLocation] = None + """The approximate location of the user.""" + + class McpAllowedToolsMcpToolFilter(BaseModel): read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -245,13 +303,14 @@ class LocalShell(BaseModel): Union[ FunctionTool, FileSearchTool, - WebSearchTool, ComputerTool, + WebSearchTool, Mcp, CodeInterpreter, ImageGeneration, LocalShell, CustomTool, + web_search_tool.WebSearchTool, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 9dde42e294..aff9359efa 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -14,6 +14,9 @@ __all__ = [ "ToolParam", + "WebSearchTool", + "WebSearchToolFilters", + "WebSearchToolUserLocation", "Mcp", "McpAllowedTools", "McpAllowedToolsMcpToolFilter", @@ -30,6 +33,61 @@ ] +class WebSearchToolFilters(TypedDict, total=False): + allowed_domains: Optional[List[str]] + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class WebSearchToolUserLocation(TypedDict, total=False): + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + +class WebSearchTool(TypedDict, total=False): + type: Required[Literal["web_search", "web_search_2025_08_26"]] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + filters: Optional[WebSearchToolFilters] + """Filters for the search.""" + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[WebSearchToolUserLocation] + """The approximate location of the user.""" + + class McpAllowedToolsMcpToolFilter(TypedDict, total=False): read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -243,13 +301,14 @@ class LocalShell(TypedDict, total=False): ToolParam: TypeAlias = Union[ FunctionToolParam, FileSearchToolParam, - WebSearchToolParam, ComputerToolParam, + WebSearchTool, Mcp, CodeInterpreter, ImageGeneration, LocalShell, CustomToolParam, + WebSearchToolParam, ] From 3154a78ac8cb404d64707d63cdfe72d3db8a45be Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 20:43:47 +0000 Subject: [PATCH 077/408] release: 1.102.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 070375331a..98411f0f2b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.101.0" + ".": "1.102.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 44b25e0a4c..26ca1c5cb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.102.0 (2025-08-26) + +Full Changelog: [v1.101.0...v1.102.0](https://github.com/openai/openai-python/compare/v1.101.0...v1.102.0) + +### Features + +* **api:** add web search filters ([1c199a8](https://github.com/openai/openai-python/commit/1c199a8dc85f773ae656fe850fdfb80b91f8f6b1)) + + +### Bug Fixes + +* avoid newer type syntax ([bd0c668](https://github.com/openai/openai-python/commit/bd0c668d754b89c78c2c9ad2e081258c04aaece6)) + + +### Chores + +* **internal:** change ci workflow machines ([3e129d5](https://github.com/openai/openai-python/commit/3e129d5e49f6391dea7497132cb3cfed8e5dd8ee)) +* **internal:** codegen related update ([b6dc170](https://github.com/openai/openai-python/commit/b6dc170832d719fc5028cfe234748c22e6e168aa)) + ## 1.101.0 (2025-08-21) Full Changelog: [v1.100.3...v1.101.0](https://github.com/openai/openai-python/compare/v1.100.3...v1.101.0) diff --git a/pyproject.toml b/pyproject.toml index 8198b178be..6736c1ad9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.101.0" +version = "1.102.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 802084af5d..b2d62263ff 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.101.0" # x-release-please-version +__version__ = "1.102.0" # x-release-please-version From 427c7c42c74654d068b2b83dc4622fe1ead92e23 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 21:53:29 +0000 Subject: [PATCH 078/408] chore(internal): update pyright exclude list --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 6736c1ad9e..fbc6c31f00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -165,6 +165,7 @@ exclude = [ "_dev", ".venv", ".nox", + ".git", # uses inline `uv` script dependencies # which means it can't be type checked From 7d0642401ec81675568d9ed2dbfb31638cfdc588 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 14:36:29 +0000 Subject: [PATCH 079/408] chore(internal): minor formatting change --- src/openai/resources/beta/threads/messages.py | 40 ++++++++-------- .../resources/beta/threads/runs/runs.py | 48 +++++++++---------- .../resources/beta/threads/runs/steps.py | 16 +++---- src/openai/resources/beta/threads/threads.py | 40 ++++++++-------- src/openai/resources/files.py | 8 ++-- 5 files changed, 76 insertions(+), 76 deletions(-) diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index 943d2e7f05..8903ff0316 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -600,27 +600,27 @@ def __init__(self, messages: Messages) -> None: self.create = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - messages.create # pyright: ignore[reportDeprecated], + messages.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - messages.retrieve # pyright: ignore[reportDeprecated], + messages.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - messages.update # pyright: ignore[reportDeprecated], + messages.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - messages.list # pyright: ignore[reportDeprecated], + messages.list, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - messages.delete # pyright: ignore[reportDeprecated], + messages.delete, # pyright: ignore[reportDeprecated], ) ) @@ -631,27 +631,27 @@ def __init__(self, messages: AsyncMessages) -> None: self.create = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - messages.create # pyright: ignore[reportDeprecated], + messages.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - messages.retrieve # pyright: ignore[reportDeprecated], + messages.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - messages.update # pyright: ignore[reportDeprecated], + messages.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - messages.list # pyright: ignore[reportDeprecated], + messages.list, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - messages.delete # pyright: ignore[reportDeprecated], + messages.delete, # pyright: ignore[reportDeprecated], ) ) @@ -662,27 +662,27 @@ def __init__(self, messages: Messages) -> None: self.create = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - messages.create # pyright: ignore[reportDeprecated], + messages.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - messages.retrieve # pyright: ignore[reportDeprecated], + messages.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - messages.update # pyright: ignore[reportDeprecated], + messages.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - messages.list # pyright: ignore[reportDeprecated], + messages.list, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - messages.delete # pyright: ignore[reportDeprecated], + messages.delete, # pyright: ignore[reportDeprecated], ) ) @@ -693,26 +693,26 @@ def __init__(self, messages: AsyncMessages) -> None: self.create = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - messages.create # pyright: ignore[reportDeprecated], + messages.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - messages.retrieve # pyright: ignore[reportDeprecated], + messages.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - messages.update # pyright: ignore[reportDeprecated], + messages.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - messages.list # pyright: ignore[reportDeprecated], + messages.list, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - messages.delete # pyright: ignore[reportDeprecated], + messages.delete, # pyright: ignore[reportDeprecated], ) ) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 07b43e6471..e97d519a40 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -2926,32 +2926,32 @@ def __init__(self, runs: Runs) -> None: self.create = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - runs.create # pyright: ignore[reportDeprecated], + runs.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - runs.retrieve # pyright: ignore[reportDeprecated], + runs.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - runs.update # pyright: ignore[reportDeprecated], + runs.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - runs.list # pyright: ignore[reportDeprecated], + runs.list, # pyright: ignore[reportDeprecated], ) ) self.cancel = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - runs.cancel # pyright: ignore[reportDeprecated], + runs.cancel, # pyright: ignore[reportDeprecated], ) ) self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - runs.submit_tool_outputs # pyright: ignore[reportDeprecated], + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], ) ) @@ -2966,32 +2966,32 @@ def __init__(self, runs: AsyncRuns) -> None: self.create = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - runs.create # pyright: ignore[reportDeprecated], + runs.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - runs.retrieve # pyright: ignore[reportDeprecated], + runs.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - runs.update # pyright: ignore[reportDeprecated], + runs.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - runs.list # pyright: ignore[reportDeprecated], + runs.list, # pyright: ignore[reportDeprecated], ) ) self.cancel = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - runs.cancel # pyright: ignore[reportDeprecated], + runs.cancel, # pyright: ignore[reportDeprecated], ) ) self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - runs.submit_tool_outputs # pyright: ignore[reportDeprecated], + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], ) ) @@ -3006,32 +3006,32 @@ def __init__(self, runs: Runs) -> None: self.create = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - runs.create # pyright: ignore[reportDeprecated], + runs.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - runs.retrieve # pyright: ignore[reportDeprecated], + runs.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - runs.update # pyright: ignore[reportDeprecated], + runs.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - runs.list # pyright: ignore[reportDeprecated], + runs.list, # pyright: ignore[reportDeprecated], ) ) self.cancel = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - runs.cancel # pyright: ignore[reportDeprecated], + runs.cancel, # pyright: ignore[reportDeprecated], ) ) self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - runs.submit_tool_outputs # pyright: ignore[reportDeprecated], + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], ) ) @@ -3046,32 +3046,32 @@ def __init__(self, runs: AsyncRuns) -> None: self.create = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - runs.create # pyright: ignore[reportDeprecated], + runs.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - runs.retrieve # pyright: ignore[reportDeprecated], + runs.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - runs.update # pyright: ignore[reportDeprecated], + runs.update, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - runs.list # pyright: ignore[reportDeprecated], + runs.list, # pyright: ignore[reportDeprecated], ) ) self.cancel = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - runs.cancel # pyright: ignore[reportDeprecated], + runs.cancel, # pyright: ignore[reportDeprecated], ) ) self.submit_tool_outputs = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - runs.submit_tool_outputs # pyright: ignore[reportDeprecated], + runs.submit_tool_outputs, # pyright: ignore[reportDeprecated], ) ) diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index eebb2003b2..8e34210bd7 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -341,12 +341,12 @@ def __init__(self, steps: Steps) -> None: self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - steps.retrieve # pyright: ignore[reportDeprecated], + steps.retrieve, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - steps.list # pyright: ignore[reportDeprecated], + steps.list, # pyright: ignore[reportDeprecated], ) ) @@ -357,12 +357,12 @@ def __init__(self, steps: AsyncSteps) -> None: self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - steps.retrieve # pyright: ignore[reportDeprecated], + steps.retrieve, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - steps.list # pyright: ignore[reportDeprecated], + steps.list, # pyright: ignore[reportDeprecated], ) ) @@ -373,12 +373,12 @@ def __init__(self, steps: Steps) -> None: self.retrieve = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - steps.retrieve # pyright: ignore[reportDeprecated], + steps.retrieve, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - steps.list # pyright: ignore[reportDeprecated], + steps.list, # pyright: ignore[reportDeprecated], ) ) @@ -389,11 +389,11 @@ def __init__(self, steps: AsyncSteps) -> None: self.retrieve = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - steps.retrieve # pyright: ignore[reportDeprecated], + steps.retrieve, # pyright: ignore[reportDeprecated], ) ) self.list = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - steps.list # pyright: ignore[reportDeprecated], + steps.list, # pyright: ignore[reportDeprecated], ) ) diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index dbe47d2d0e..7121851cab 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -1785,27 +1785,27 @@ def __init__(self, threads: Threads) -> None: self.create = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - threads.create # pyright: ignore[reportDeprecated], + threads.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - threads.retrieve # pyright: ignore[reportDeprecated], + threads.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - threads.update # pyright: ignore[reportDeprecated], + threads.update, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - threads.delete # pyright: ignore[reportDeprecated], + threads.delete, # pyright: ignore[reportDeprecated], ) ) self.create_and_run = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - threads.create_and_run # pyright: ignore[reportDeprecated], + threads.create_and_run, # pyright: ignore[reportDeprecated], ) ) @@ -1824,27 +1824,27 @@ def __init__(self, threads: AsyncThreads) -> None: self.create = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - threads.create # pyright: ignore[reportDeprecated], + threads.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - threads.retrieve # pyright: ignore[reportDeprecated], + threads.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - threads.update # pyright: ignore[reportDeprecated], + threads.update, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - threads.delete # pyright: ignore[reportDeprecated], + threads.delete, # pyright: ignore[reportDeprecated], ) ) self.create_and_run = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - threads.create_and_run # pyright: ignore[reportDeprecated], + threads.create_and_run, # pyright: ignore[reportDeprecated], ) ) @@ -1863,27 +1863,27 @@ def __init__(self, threads: Threads) -> None: self.create = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - threads.create # pyright: ignore[reportDeprecated], + threads.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - threads.retrieve # pyright: ignore[reportDeprecated], + threads.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - threads.update # pyright: ignore[reportDeprecated], + threads.update, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - threads.delete # pyright: ignore[reportDeprecated], + threads.delete, # pyright: ignore[reportDeprecated], ) ) self.create_and_run = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - threads.create_and_run # pyright: ignore[reportDeprecated], + threads.create_and_run, # pyright: ignore[reportDeprecated], ) ) @@ -1902,27 +1902,27 @@ def __init__(self, threads: AsyncThreads) -> None: self.create = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - threads.create # pyright: ignore[reportDeprecated], + threads.create, # pyright: ignore[reportDeprecated], ) ) self.retrieve = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - threads.retrieve # pyright: ignore[reportDeprecated], + threads.retrieve, # pyright: ignore[reportDeprecated], ) ) self.update = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - threads.update # pyright: ignore[reportDeprecated], + threads.update, # pyright: ignore[reportDeprecated], ) ) self.delete = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - threads.delete # pyright: ignore[reportDeprecated], + threads.delete, # pyright: ignore[reportDeprecated], ) ) self.create_and_run = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - threads.create_and_run # pyright: ignore[reportDeprecated], + threads.create_and_run, # pyright: ignore[reportDeprecated], ) ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index b45b8f303f..963c3c0a9f 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -687,7 +687,7 @@ def __init__(self, files: Files) -> None: ) self.retrieve_content = ( # pyright: ignore[reportDeprecated] _legacy_response.to_raw_response_wrapper( - files.retrieve_content # pyright: ignore[reportDeprecated], + files.retrieve_content, # pyright: ignore[reportDeprecated], ) ) @@ -713,7 +713,7 @@ def __init__(self, files: AsyncFiles) -> None: ) self.retrieve_content = ( # pyright: ignore[reportDeprecated] _legacy_response.async_to_raw_response_wrapper( - files.retrieve_content # pyright: ignore[reportDeprecated], + files.retrieve_content, # pyright: ignore[reportDeprecated], ) ) @@ -740,7 +740,7 @@ def __init__(self, files: Files) -> None: ) self.retrieve_content = ( # pyright: ignore[reportDeprecated] to_streamed_response_wrapper( - files.retrieve_content # pyright: ignore[reportDeprecated], + files.retrieve_content, # pyright: ignore[reportDeprecated], ) ) @@ -767,6 +767,6 @@ def __init__(self, files: AsyncFiles) -> None: ) self.retrieve_content = ( # pyright: ignore[reportDeprecated] async_to_streamed_response_wrapper( - files.retrieve_content # pyright: ignore[reportDeprecated], + files.retrieve_content, # pyright: ignore[reportDeprecated], ) ) From 061ebd6325b5fabaa560d7c1432c99a284efc337 Mon Sep 17 00:00:00 2001 From: Kar Petrosyan <92274156+karpetrosyan@users.noreply.github.com> Date: Wed, 27 Aug 2025 23:29:21 +0400 Subject: [PATCH 080/408] chore: bump `inline-snapshot` version to 0.28.0 (#2590) --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- tests/lib/chat/test_completions_streaming.py | 3 ++- tests/lib/snapshots.py | 3 +-- tests/lib/utils.py | 12 ------------ 5 files changed, 5 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fbc6c31f00..2633918fc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ dev-dependencies = [ "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", "rich>=13.7.1", - "inline-snapshot >=0.7.0", + "inline-snapshot>=0.28.0", "azure-identity >=1.14.1", "types-tqdm > 4", "types-pyaudio > 0", diff --git a/requirements-dev.lock b/requirements-dev.lock index e8bea53014..669378387d 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -90,7 +90,7 @@ idna==3.4 importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest -inline-snapshot==0.27.0 +inline-snapshot==0.28.0 jiter==0.5.0 # via openai markdown-it-py==3.0.0 diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index fa17f67177..548416dfe2 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -13,6 +13,7 @@ external, snapshot, outsource, # pyright: ignore[reportUnknownVariableType] + get_snapshot_value, ) import openai @@ -30,7 +31,7 @@ ) from openai.lib._parsing._completions import ResponseFormatT -from ..utils import print_obj, get_snapshot_value +from ..utils import print_obj from ...conftest import base_url _T = TypeVar("_T") diff --git a/tests/lib/snapshots.py b/tests/lib/snapshots.py index ed53edebcb..91222acda1 100644 --- a/tests/lib/snapshots.py +++ b/tests/lib/snapshots.py @@ -7,11 +7,10 @@ import httpx from respx import MockRouter +from inline_snapshot import get_snapshot_value from openai import OpenAI, AsyncOpenAI -from .utils import get_snapshot_value - _T = TypeVar("_T") diff --git a/tests/lib/utils.py b/tests/lib/utils.py index 2129ee811a..e6b6a29434 100644 --- a/tests/lib/utils.py +++ b/tests/lib/utils.py @@ -52,15 +52,3 @@ def get_caller_name(*, stacklevel: int = 1) -> str: def clear_locals(string: str, *, stacklevel: int) -> str: caller = get_caller_name(stacklevel=stacklevel + 1) return string.replace(f"{caller}..", "") - - -def get_snapshot_value(snapshot: Any) -> Any: - if not hasattr(snapshot, "_old_value"): - return snapshot - - old = snapshot._old_value - if not hasattr(old, "value"): - return old - - loader = getattr(old.value, "_load_value", None) - return loader() if loader else old.value From 845466f6caddcf3eceb31e770298cecd3f4f0a6e Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Wed, 27 Aug 2025 16:27:07 -0400 Subject: [PATCH 081/408] fix(responses): add missing params to stream() method --- src/openai/resources/responses/responses.py | 76 ++++++++++++++++++--- tests/lib/responses/test_responses.py | 24 ++++++- 2 files changed, 89 insertions(+), 11 deletions(-) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 062fd491f2..e04382a9ff 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -31,7 +31,6 @@ parse_response, type_to_text_format_param as _type_to_text_format_param, ) -from ...types.shared.chat_model import ChatModel from ...types.responses.response import Response from ...types.responses.tool_param import ToolParam, ParseableToolParam from ...types.shared_params.metadata import Metadata @@ -881,22 +880,29 @@ def stream( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, background: Optional[bool] | NotGiven = NOT_GIVEN, text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -913,22 +919,29 @@ def stream( *, response_id: str | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel] | NotGiven = NOT_GIVEN, + model: ResponsesModel | NotGiven = NOT_GIVEN, background: Optional[bool] | NotGiven = NOT_GIVEN, text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -943,18 +956,25 @@ def stream( new_response_args = { "input": input, "model": model, + "conversation": conversation, "include": include, "instructions": instructions, "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, "metadata": metadata, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, "store": store, "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, + "top_logprobs": top_logprobs, "top_p": top_p, "truncation": truncation, "user": user, @@ -989,12 +1009,16 @@ def stream( input=input, model=model, tools=tools, + conversation=conversation, include=include, instructions=instructions, max_output_tokens=max_output_tokens, + max_tool_calls=max_tool_calls, metadata=metadata, parallel_tool_calls=parallel_tool_calls, previous_response_id=previous_response_id, + prompt=prompt, + prompt_cache_key=prompt_cache_key, store=store, stream_options=stream_options, stream=True, @@ -1002,6 +1026,9 @@ def stream( text=text, tool_choice=tool_choice, reasoning=reasoning, + safety_identifier=safety_identifier, + service_tier=service_tier, + top_logprobs=top_logprobs, top_p=top_p, truncation=truncation, user=user, @@ -1057,7 +1084,7 @@ def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, @@ -2275,22 +2302,29 @@ def stream( self, *, input: Union[str, ResponseInputParam], - model: Union[str, ChatModel], + model: ResponsesModel, background: Optional[bool] | NotGiven = NOT_GIVEN, text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -2307,22 +2341,29 @@ def stream( *, response_id: str | NotGiven = NOT_GIVEN, input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel] | NotGiven = NOT_GIVEN, + model: ResponsesModel | NotGiven = NOT_GIVEN, background: Optional[bool] | NotGiven = NOT_GIVEN, text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, instructions: Optional[str] | NotGiven = NOT_GIVEN, max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, + max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, + prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, + prompt_cache_key: str | NotGiven = NOT_GIVEN, reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, + safety_identifier: str | NotGiven = NOT_GIVEN, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, + top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, top_p: Optional[float] | NotGiven = NOT_GIVEN, truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, user: str | NotGiven = NOT_GIVEN, @@ -2337,18 +2378,25 @@ def stream( new_response_args = { "input": input, "model": model, + "conversation": conversation, "include": include, "instructions": instructions, "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, "metadata": metadata, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, "store": store, "stream_options": stream_options, "temperature": temperature, "text": text, "tool_choice": tool_choice, + "top_logprobs": top_logprobs, "top_p": top_p, "truncation": truncation, "user": user, @@ -2384,21 +2432,29 @@ def stream( model=model, stream=True, tools=tools, + conversation=conversation, include=include, instructions=instructions, max_output_tokens=max_output_tokens, + max_tool_calls=max_tool_calls, metadata=metadata, parallel_tool_calls=parallel_tool_calls, previous_response_id=previous_response_id, + prompt=prompt, + prompt_cache_key=prompt_cache_key, store=store, stream_options=stream_options, temperature=temperature, text=text, tool_choice=tool_choice, reasoning=reasoning, + safety_identifier=safety_identifier, + service_tier=service_tier, + top_logprobs=top_logprobs, top_p=top_p, truncation=truncation, user=user, + background=background, extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, @@ -2455,7 +2511,7 @@ async def parse( stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam| NotGiven = NOT_GIVEN, + text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py index 8ce3462e76..31b6e55ddc 100644 --- a/tests/lib/responses/test_responses.py +++ b/tests/lib/responses/test_responses.py @@ -6,7 +6,8 @@ from respx import MockRouter from inline_snapshot import snapshot -from openai import OpenAI +from openai import OpenAI, AsyncOpenAI +from openai._utils import assert_signatures_in_sync from ...conftest import base_url from ..snapshots import make_snapshot_request @@ -38,3 +39,24 @@ def test_output_text(client: OpenAI, respx_mock: MockRouter) -> None: assert response.output_text == snapshot( "I can't provide real-time updates, but you can easily check the current weather in San Francisco using a weather website or app. Typically, San Francisco has cool, foggy summers and mild winters, so it's good to be prepared for variable weather!" ) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + assert_signatures_in_sync( + checking_client.responses.create, + checking_client.responses.stream, + exclude_params={"stream", "tools"}, + ) + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_parse_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + assert_signatures_in_sync( + checking_client.responses.create, + checking_client.responses.parse, + exclude_params={"tools"}, + ) From 2843a64c9c26a720a931845a83302c72b85f241b Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Fri, 29 Aug 2025 14:45:59 -0400 Subject: [PATCH 082/408] chore(internal): fix formatting --- tests/lib/responses/test_responses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py index 31b6e55ddc..8e5f16df95 100644 --- a/tests/lib/responses/test_responses.py +++ b/tests/lib/responses/test_responses.py @@ -51,6 +51,7 @@ def test_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_clie exclude_params={"stream", "tools"}, ) + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_parse_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: checking_client: OpenAI | AsyncOpenAI = client if sync else async_client From 463e870dcd4ddf94dafb4808850c6fdbecd36a88 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 29 Aug 2025 19:14:21 +0000 Subject: [PATCH 083/408] chore(internal): add Sequence related utils --- src/openai/_types.py | 36 ++++++++++++++++++++++++++++++++++- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_typing.py | 5 +++++ tests/utils.py | 10 +++++++++- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/openai/_types.py b/src/openai/_types.py index 5dae55f4a9..0e8ffa12aa 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -13,10 +13,21 @@ Mapping, TypeVar, Callable, + Iterator, Optional, Sequence, ) -from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) import httpx import pydantic @@ -219,3 +230,26 @@ class _GenericAlias(Protocol): class HttpxSendArgs(TypedDict, total=False): auth: httpx.Auth follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... + def count(self, value: Any, /) -> int: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index bd01c088dc..6471aa4c0d 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -41,6 +41,7 @@ extract_type_arg as extract_type_arg, is_iterable_type as is_iterable_type, is_required_type as is_required_type, + is_sequence_type as is_sequence_type, is_annotated_type as is_annotated_type, is_type_alias_type as is_type_alias_type, strip_annotated_type as strip_annotated_type, diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index 1bac9542e2..845cd6b287 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -26,6 +26,11 @@ def is_list_type(typ: type) -> bool: return (get_origin(typ) or typ) == list +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + def is_iterable_type(typ: type) -> bool: """If the given type is `typing.Iterable[T]`""" origin = get_origin(typ) or typ diff --git a/tests/utils.py b/tests/utils.py index 4cf5ce171b..7740ed3f7c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -5,7 +5,7 @@ import inspect import traceback import contextlib -from typing import Any, TypeVar, Iterator, ForwardRef, cast +from typing import Any, TypeVar, Iterator, ForwardRef, Sequence, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type @@ -18,6 +18,7 @@ is_list_type, is_union_type, extract_type_arg, + is_sequence_type, is_annotated_type, is_type_alias_type, ) @@ -78,6 +79,13 @@ def assert_matches_type( if is_list_type(type_): return _assert_list_type(type_, value) + if is_sequence_type(type_): + assert isinstance(value, Sequence) + inner_type = get_args(type_)[0] + for entry in value: # type: ignore + assert_type(inner_type, entry) # type: ignore + return + if origin == str: assert isinstance(value, str) elif origin == int: From 3d3d16ab5de830665adf13df82c991b60385531d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 13:46:20 +0000 Subject: [PATCH 084/408] feat(api): realtime API updates --- .stats.yml | 8 +- README.md | 6 +- api.md | 111 ++ examples/realtime/audio_util.py | 2 +- examples/realtime/azure_realtime.py | 16 +- examples/realtime/push_to_talk_app.py | 20 +- examples/realtime/realtime.py | 54 + src/openai/__init__.py | 1 + src/openai/_client.py | 38 + src/openai/_module_client.py | 8 + src/openai/resources/audio/speech.py | 8 +- src/openai/resources/beta/beta.py | 20 - src/openai/resources/realtime/__init__.py | 33 + .../resources/realtime/client_secrets.py | 185 +++ src/openai/resources/realtime/realtime.py | 1056 +++++++++++++++++ src/openai/resources/responses/responses.py | 30 +- .../types/audio/speech_create_params.py | 4 +- .../types/chat/chat_completion_audio_param.py | 4 +- src/openai/types/realtime/__init__.py | 184 +++ .../realtime/client_secret_create_params.py | 39 + .../realtime/client_secret_create_response.py | 110 ++ .../realtime/conversation_created_event.py | 27 + .../types/realtime/conversation_item.py | 32 + .../types/realtime/conversation_item_added.py | 26 + .../conversation_item_create_event.py | 29 + .../conversation_item_create_event_param.py | 29 + .../conversation_item_created_event.py | 27 + .../conversation_item_delete_event.py | 19 + .../conversation_item_delete_event_param.py | 18 + .../conversation_item_deleted_event.py | 18 + .../types/realtime/conversation_item_done.py | 26 + ...put_audio_transcription_completed_event.py | 76 ++ ...m_input_audio_transcription_delta_event.py | 29 + ..._input_audio_transcription_failed_event.py | 39 + ..._item_input_audio_transcription_segment.py | 36 + .../types/realtime/conversation_item_param.py | 30 + .../conversation_item_retrieve_event.py | 19 + .../conversation_item_retrieve_event_param.py | 18 + .../conversation_item_truncate_event.py | 32 + .../conversation_item_truncate_event_param.py | 31 + .../conversation_item_truncated_event.py | 24 + .../input_audio_buffer_append_event.py | 23 + .../input_audio_buffer_append_event_param.py | 22 + .../input_audio_buffer_clear_event.py | 16 + .../input_audio_buffer_clear_event_param.py | 15 + .../input_audio_buffer_cleared_event.py | 15 + .../input_audio_buffer_commit_event.py | 16 + .../input_audio_buffer_commit_event_param.py | 15 + .../input_audio_buffer_committed_event.py | 25 + ...input_audio_buffer_speech_started_event.py | 26 + ...input_audio_buffer_speech_stopped_event.py | 25 + .../input_audio_buffer_timeout_triggered.py | 24 + .../types/realtime/log_prob_properties.py | 18 + .../realtime/mcp_list_tools_completed.py | 18 + .../types/realtime/mcp_list_tools_failed.py | 18 + .../realtime/mcp_list_tools_in_progress.py | 18 + .../output_audio_buffer_clear_event.py | 16 + .../output_audio_buffer_clear_event_param.py | 15 + .../realtime/rate_limits_updated_event.py | 33 + .../types/realtime/realtime_audio_config.py | 184 +++ .../realtime/realtime_audio_config_param.py | 187 +++ .../types/realtime/realtime_client_event.py | 38 + .../realtime/realtime_client_event_param.py | 36 + .../realtime/realtime_client_secret_config.py | 27 + .../realtime_client_secret_config_param.py | 26 + .../types/realtime/realtime_connect_params.py | 11 + ...ime_conversation_item_assistant_message.py | 36 + ...nversation_item_assistant_message_param.py | 36 + ...ealtime_conversation_item_function_call.py | 31 + ..._conversation_item_function_call_output.py | 28 + ...rsation_item_function_call_output_param.py | 27 + ...e_conversation_item_function_call_param.py | 30 + ...altime_conversation_item_system_message.py | 36 + ..._conversation_item_system_message_param.py | 36 + ...realtime_conversation_item_user_message.py | 42 + ...me_conversation_item_user_message_param.py | 42 + src/openai/types/realtime/realtime_error.py | 24 + .../types/realtime/realtime_error_event.py | 19 + .../realtime/realtime_mcp_approval_request.py | 24 + .../realtime_mcp_approval_request_param.py | 24 + .../realtime_mcp_approval_response.py | 25 + .../realtime_mcp_approval_response_param.py | 25 + .../types/realtime/realtime_mcp_list_tools.py | 36 + .../realtime/realtime_mcp_list_tools_param.py | 36 + .../realtime/realtime_mcp_protocol_error.py | 15 + .../realtime_mcp_protocol_error_param.py | 15 + .../types/realtime/realtime_mcp_tool_call.py | 43 + .../realtime/realtime_mcp_tool_call_param.py | 40 + .../realtime_mcp_tool_execution_error.py | 13 + ...realtime_mcp_tool_execution_error_param.py | 13 + .../types/realtime/realtime_mcphttp_error.py | 15 + .../realtime/realtime_mcphttp_error_param.py | 15 + .../types/realtime/realtime_response.py | 89 ++ .../realtime/realtime_response_status.py | 39 + .../types/realtime/realtime_response_usage.py | 35 + ...time_response_usage_input_token_details.py | 18 + ...ime_response_usage_output_token_details.py | 15 + .../types/realtime/realtime_server_event.py | 159 +++ src/openai/types/realtime/realtime_session.py | 305 +++++ .../realtime_session_create_request.py | 116 ++ .../realtime_session_create_request_param.py | 119 ++ .../realtime_session_create_response.py | 222 ++++ .../realtime/realtime_tool_choice_config.py | 12 + .../realtime_tool_choice_config_param.py | 14 + .../types/realtime/realtime_tools_config.py | 10 + .../realtime/realtime_tools_config_param.py | 158 +++ .../realtime/realtime_tools_config_union.py | 158 +++ .../realtime_tools_config_union_param.py | 155 +++ .../types/realtime/realtime_tracing_config.py | 31 + .../realtime/realtime_tracing_config_param.py | 31 + ...me_transcription_session_create_request.py | 128 ++ ...nscription_session_create_request_param.py | 128 ++ .../types/realtime/realtime_truncation.py | 22 + .../realtime/realtime_truncation_param.py | 22 + .../realtime/response_audio_delta_event.py | 30 + .../realtime/response_audio_done_event.py | 27 + .../response_audio_transcript_delta_event.py | 30 + .../response_audio_transcript_done_event.py | 30 + .../types/realtime/response_cancel_event.py | 22 + .../realtime/response_cancel_event_param.py | 21 + .../response_content_part_added_event.py | 45 + .../response_content_part_done_event.py | 45 + .../types/realtime/response_create_event.py | 134 +++ .../realtime/response_create_event_param.py | 133 +++ .../types/realtime/response_created_event.py | 19 + .../types/realtime/response_done_event.py | 19 + ...nse_function_call_arguments_delta_event.py | 30 + ...onse_function_call_arguments_done_event.py | 30 + .../response_mcp_call_arguments_delta.py | 31 + .../response_mcp_call_arguments_done.py | 27 + .../realtime/response_mcp_call_completed.py | 21 + .../realtime/response_mcp_call_failed.py | 21 + .../realtime/response_mcp_call_in_progress.py | 21 + .../response_output_item_added_event.py | 25 + .../response_output_item_done_event.py | 25 + .../realtime/response_text_delta_event.py | 30 + .../realtime/response_text_done_event.py | 30 + .../types/realtime/session_created_event.py | 19 + .../types/realtime/session_update_event.py | 20 + .../realtime/session_update_event_param.py | 20 + .../types/realtime/session_updated_event.py | 19 + .../realtime/transcription_session_created.py | 105 ++ .../realtime/transcription_session_update.py | 20 + .../transcription_session_update_param.py | 20 + .../transcription_session_updated_event.py | 105 ++ src/openai/types/responses/__init__.py | 2 + src/openai/types/responses/response.py | 5 +- .../types/responses/response_create_params.py | 5 +- src/openai/types/responses/tool.py | 62 +- src/openai/types/responses/tool_param.py | 63 +- .../responses/web_search_preview_tool.py | 49 + .../web_search_preview_tool_param.py | 49 + src/openai/types/responses/web_search_tool.py | 30 +- .../types/responses/web_search_tool_param.py | 30 +- src/openai/types/webhooks/__init__.py | 1 + .../realtime_call_incoming_webhook_event.py | 41 + .../types/webhooks/unwrap_webhook_event.py | 2 + .../beta/realtime/test_sessions.py | 166 --- .../realtime/test_transcription_sessions.py | 134 --- tests/api_resources/beta/test_realtime.py | 2 + .../{beta => }/realtime/__init__.py | 0 .../realtime/test_client_secrets.py | 208 ++++ tests/api_resources/test_realtime.py | 19 + 163 files changed, 7657 insertions(+), 486 deletions(-) create mode 100755 examples/realtime/realtime.py create mode 100644 src/openai/resources/realtime/__init__.py create mode 100644 src/openai/resources/realtime/client_secrets.py create mode 100644 src/openai/resources/realtime/realtime.py create mode 100644 src/openai/types/realtime/__init__.py create mode 100644 src/openai/types/realtime/client_secret_create_params.py create mode 100644 src/openai/types/realtime/client_secret_create_response.py create mode 100644 src/openai/types/realtime/conversation_created_event.py create mode 100644 src/openai/types/realtime/conversation_item.py create mode 100644 src/openai/types/realtime/conversation_item_added.py create mode 100644 src/openai/types/realtime/conversation_item_create_event.py create mode 100644 src/openai/types/realtime/conversation_item_create_event_param.py create mode 100644 src/openai/types/realtime/conversation_item_created_event.py create mode 100644 src/openai/types/realtime/conversation_item_delete_event.py create mode 100644 src/openai/types/realtime/conversation_item_delete_event_param.py create mode 100644 src/openai/types/realtime/conversation_item_deleted_event.py create mode 100644 src/openai/types/realtime/conversation_item_done.py create mode 100644 src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py create mode 100644 src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py create mode 100644 src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py create mode 100644 src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py create mode 100644 src/openai/types/realtime/conversation_item_param.py create mode 100644 src/openai/types/realtime/conversation_item_retrieve_event.py create mode 100644 src/openai/types/realtime/conversation_item_retrieve_event_param.py create mode 100644 src/openai/types/realtime/conversation_item_truncate_event.py create mode 100644 src/openai/types/realtime/conversation_item_truncate_event_param.py create mode 100644 src/openai/types/realtime/conversation_item_truncated_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_append_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_append_event_param.py create mode 100644 src/openai/types/realtime/input_audio_buffer_clear_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_clear_event_param.py create mode 100644 src/openai/types/realtime/input_audio_buffer_cleared_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_commit_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_commit_event_param.py create mode 100644 src/openai/types/realtime/input_audio_buffer_committed_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_speech_started_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py create mode 100644 src/openai/types/realtime/input_audio_buffer_timeout_triggered.py create mode 100644 src/openai/types/realtime/log_prob_properties.py create mode 100644 src/openai/types/realtime/mcp_list_tools_completed.py create mode 100644 src/openai/types/realtime/mcp_list_tools_failed.py create mode 100644 src/openai/types/realtime/mcp_list_tools_in_progress.py create mode 100644 src/openai/types/realtime/output_audio_buffer_clear_event.py create mode 100644 src/openai/types/realtime/output_audio_buffer_clear_event_param.py create mode 100644 src/openai/types/realtime/rate_limits_updated_event.py create mode 100644 src/openai/types/realtime/realtime_audio_config.py create mode 100644 src/openai/types/realtime/realtime_audio_config_param.py create mode 100644 src/openai/types/realtime/realtime_client_event.py create mode 100644 src/openai/types/realtime/realtime_client_event_param.py create mode 100644 src/openai/types/realtime/realtime_client_secret_config.py create mode 100644 src/openai/types/realtime/realtime_client_secret_config_param.py create mode 100644 src/openai/types/realtime/realtime_connect_params.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_assistant_message.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_function_call.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_function_call_output.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_function_call_param.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_system_message.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_system_message_param.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_user_message.py create mode 100644 src/openai/types/realtime/realtime_conversation_item_user_message_param.py create mode 100644 src/openai/types/realtime/realtime_error.py create mode 100644 src/openai/types/realtime/realtime_error_event.py create mode 100644 src/openai/types/realtime/realtime_mcp_approval_request.py create mode 100644 src/openai/types/realtime/realtime_mcp_approval_request_param.py create mode 100644 src/openai/types/realtime/realtime_mcp_approval_response.py create mode 100644 src/openai/types/realtime/realtime_mcp_approval_response_param.py create mode 100644 src/openai/types/realtime/realtime_mcp_list_tools.py create mode 100644 src/openai/types/realtime/realtime_mcp_list_tools_param.py create mode 100644 src/openai/types/realtime/realtime_mcp_protocol_error.py create mode 100644 src/openai/types/realtime/realtime_mcp_protocol_error_param.py create mode 100644 src/openai/types/realtime/realtime_mcp_tool_call.py create mode 100644 src/openai/types/realtime/realtime_mcp_tool_call_param.py create mode 100644 src/openai/types/realtime/realtime_mcp_tool_execution_error.py create mode 100644 src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py create mode 100644 src/openai/types/realtime/realtime_mcphttp_error.py create mode 100644 src/openai/types/realtime/realtime_mcphttp_error_param.py create mode 100644 src/openai/types/realtime/realtime_response.py create mode 100644 src/openai/types/realtime/realtime_response_status.py create mode 100644 src/openai/types/realtime/realtime_response_usage.py create mode 100644 src/openai/types/realtime/realtime_response_usage_input_token_details.py create mode 100644 src/openai/types/realtime/realtime_response_usage_output_token_details.py create mode 100644 src/openai/types/realtime/realtime_server_event.py create mode 100644 src/openai/types/realtime/realtime_session.py create mode 100644 src/openai/types/realtime/realtime_session_create_request.py create mode 100644 src/openai/types/realtime/realtime_session_create_request_param.py create mode 100644 src/openai/types/realtime/realtime_session_create_response.py create mode 100644 src/openai/types/realtime/realtime_tool_choice_config.py create mode 100644 src/openai/types/realtime/realtime_tool_choice_config_param.py create mode 100644 src/openai/types/realtime/realtime_tools_config.py create mode 100644 src/openai/types/realtime/realtime_tools_config_param.py create mode 100644 src/openai/types/realtime/realtime_tools_config_union.py create mode 100644 src/openai/types/realtime/realtime_tools_config_union_param.py create mode 100644 src/openai/types/realtime/realtime_tracing_config.py create mode 100644 src/openai/types/realtime/realtime_tracing_config_param.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_create_request.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_create_request_param.py create mode 100644 src/openai/types/realtime/realtime_truncation.py create mode 100644 src/openai/types/realtime/realtime_truncation_param.py create mode 100644 src/openai/types/realtime/response_audio_delta_event.py create mode 100644 src/openai/types/realtime/response_audio_done_event.py create mode 100644 src/openai/types/realtime/response_audio_transcript_delta_event.py create mode 100644 src/openai/types/realtime/response_audio_transcript_done_event.py create mode 100644 src/openai/types/realtime/response_cancel_event.py create mode 100644 src/openai/types/realtime/response_cancel_event_param.py create mode 100644 src/openai/types/realtime/response_content_part_added_event.py create mode 100644 src/openai/types/realtime/response_content_part_done_event.py create mode 100644 src/openai/types/realtime/response_create_event.py create mode 100644 src/openai/types/realtime/response_create_event_param.py create mode 100644 src/openai/types/realtime/response_created_event.py create mode 100644 src/openai/types/realtime/response_done_event.py create mode 100644 src/openai/types/realtime/response_function_call_arguments_delta_event.py create mode 100644 src/openai/types/realtime/response_function_call_arguments_done_event.py create mode 100644 src/openai/types/realtime/response_mcp_call_arguments_delta.py create mode 100644 src/openai/types/realtime/response_mcp_call_arguments_done.py create mode 100644 src/openai/types/realtime/response_mcp_call_completed.py create mode 100644 src/openai/types/realtime/response_mcp_call_failed.py create mode 100644 src/openai/types/realtime/response_mcp_call_in_progress.py create mode 100644 src/openai/types/realtime/response_output_item_added_event.py create mode 100644 src/openai/types/realtime/response_output_item_done_event.py create mode 100644 src/openai/types/realtime/response_text_delta_event.py create mode 100644 src/openai/types/realtime/response_text_done_event.py create mode 100644 src/openai/types/realtime/session_created_event.py create mode 100644 src/openai/types/realtime/session_update_event.py create mode 100644 src/openai/types/realtime/session_update_event_param.py create mode 100644 src/openai/types/realtime/session_updated_event.py create mode 100644 src/openai/types/realtime/transcription_session_created.py create mode 100644 src/openai/types/realtime/transcription_session_update.py create mode 100644 src/openai/types/realtime/transcription_session_update_param.py create mode 100644 src/openai/types/realtime/transcription_session_updated_event.py create mode 100644 src/openai/types/responses/web_search_preview_tool.py create mode 100644 src/openai/types/responses/web_search_preview_tool_param.py create mode 100644 src/openai/types/webhooks/realtime_call_incoming_webhook_event.py delete mode 100644 tests/api_resources/beta/realtime/test_sessions.py delete mode 100644 tests/api_resources/beta/realtime/test_transcription_sessions.py rename tests/api_resources/{beta => }/realtime/__init__.py (100%) create mode 100644 tests/api_resources/realtime/test_client_secrets.py create mode 100644 tests/api_resources/test_realtime.py diff --git a/.stats.yml b/.stats.yml index 5ad90ac5ab..ebe81d146e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 119 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-8517ffa1004e31ca2523d617629e64be6fe4f13403ddfd9db5b3be002656cbde.yml -openapi_spec_hash: b64dd8c8b23082a7aa2a3e5c5fffd8bd -config_hash: fe0ea26680ac2075a6cd66416aefe7db +configured_endpoints: 118 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-356b4364203ff36d7724074cd04f6e684253bfcc3c9d969122d730aa7bc51b46.yml +openapi_spec_hash: 4ab8e96f52699bc3d2b0c4432aa92af8 +config_hash: b854932c0ea24b400bdd64e4376936bd diff --git a/README.md b/README.md index d4b8d8d170..9311b477a3 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ async def main(): asyncio.run(main()) ``` -## Realtime API beta +## Realtime API 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. @@ -243,7 +243,7 @@ from openai import AsyncOpenAI async def main(): client = AsyncOpenAI() - async with client.beta.realtime.connect(model="gpt-4o-realtime-preview") as connection: + async with client.realtime.connect(model="gpt-realtime") as connection: await connection.session.update(session={'modalities': ['text']}) await connection.conversation.item.create( @@ -277,7 +277,7 @@ Whenever an error occurs, the Realtime API will send an [`error` event](https:// ```py client = AsyncOpenAI() -async with client.beta.realtime.connect(model="gpt-4o-realtime-preview") as connection: +async with client.realtime.connect(model="gpt-realtime") as connection: ... async for event in connection: if event.type == 'error': diff --git a/api.md b/api.md index 7eb62e67f2..a8a95bd23e 100644 --- a/api.md +++ b/api.md @@ -431,6 +431,7 @@ from openai.types.webhooks import ( FineTuningJobCancelledWebhookEvent, FineTuningJobFailedWebhookEvent, FineTuningJobSucceededWebhookEvent, + RealtimeCallIncomingWebhookEvent, ResponseCancelledWebhookEvent, ResponseCompletedWebhookEvent, ResponseFailedWebhookEvent, @@ -832,6 +833,7 @@ from openai.types.responses import ( ToolChoiceMcp, ToolChoiceOptions, ToolChoiceTypes, + WebSearchPreviewTool, WebSearchTool, ) ``` @@ -855,6 +857,115 @@ Methods: - client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] +# Realtime + +Types: + +```python +from openai.types.realtime import ( + ConversationCreatedEvent, + ConversationItem, + ConversationItemAdded, + ConversationItemCreateEvent, + ConversationItemCreatedEvent, + ConversationItemDeleteEvent, + ConversationItemDeletedEvent, + ConversationItemDone, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemInputAudioTranscriptionSegment, + ConversationItemRetrieveEvent, + ConversationItemTruncateEvent, + ConversationItemTruncatedEvent, + ConversationItemWithReference, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommitEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + InputAudioBufferTimeoutTriggered, + LogProbProperties, + McpListToolsCompleted, + McpListToolsFailed, + McpListToolsInProgress, + OutputAudioBufferClearEvent, + RateLimitsUpdatedEvent, + RealtimeAudioConfig, + RealtimeClientEvent, + RealtimeClientSecretConfig, + RealtimeConversationItemAssistantMessage, + RealtimeConversationItemFunctionCall, + RealtimeConversationItemFunctionCallOutput, + RealtimeConversationItemSystemMessage, + RealtimeConversationItemUserMessage, + RealtimeError, + RealtimeErrorEvent, + RealtimeMcpApprovalRequest, + RealtimeMcpApprovalResponse, + RealtimeMcpListTools, + RealtimeMcpProtocolError, + RealtimeMcpToolCall, + RealtimeMcpToolExecutionError, + RealtimeMcphttpError, + RealtimeResponse, + RealtimeResponseStatus, + RealtimeResponseUsage, + RealtimeResponseUsageInputTokenDetails, + RealtimeResponseUsageOutputTokenDetails, + RealtimeServerEvent, + RealtimeSession, + RealtimeSessionCreateRequest, + RealtimeToolChoiceConfig, + RealtimeToolsConfig, + RealtimeToolsConfigUnion, + RealtimeTracingConfig, + RealtimeTranscriptionSessionCreateRequest, + RealtimeTruncation, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCancelEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreateEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseMcpCallArgumentsDelta, + ResponseMcpCallArgumentsDone, + ResponseMcpCallCompleted, + ResponseMcpCallFailed, + ResponseMcpCallInProgress, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + SessionCreatedEvent, + SessionUpdateEvent, + SessionUpdatedEvent, + TranscriptionSessionCreated, + TranscriptionSessionUpdate, + TranscriptionSessionUpdatedEvent, +) +``` + +## ClientSecrets + +Types: + +```python +from openai.types.realtime import RealtimeSessionCreateResponse, ClientSecretCreateResponse +``` + +Methods: + +- client.realtime.client_secrets.create(\*\*params) -> ClientSecretCreateResponse + # Conversations Types: diff --git a/examples/realtime/audio_util.py b/examples/realtime/audio_util.py index b073cc45be..954a508675 100644 --- a/examples/realtime/audio_util.py +++ b/examples/realtime/audio_util.py @@ -11,7 +11,7 @@ import sounddevice as sd from pydub import AudioSegment -from openai.resources.beta.realtime.realtime import AsyncRealtimeConnection +from openai.resources.realtime.realtime import AsyncRealtimeConnection CHUNK_LENGTH_S = 0.05 # 100ms SAMPLE_RATE = 24000 diff --git a/examples/realtime/azure_realtime.py b/examples/realtime/azure_realtime.py index de88d47052..3cf64b8be9 100644 --- a/examples/realtime/azure_realtime.py +++ b/examples/realtime/azure_realtime.py @@ -26,10 +26,16 @@ async def main() -> None: azure_ad_token_provider=get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default"), api_version="2024-10-01-preview", ) - async with client.beta.realtime.connect( - model="gpt-4o-realtime-preview", # deployment name for your model + async with client.realtime.connect( + model="gpt-realtime", # deployment name for your model ) as connection: - await connection.session.update(session={"modalities": ["text"]}) # type: ignore + await connection.session.update( + session={ + "output_modalities": ["text"], + "model": "gpt-realtime", + "type": "realtime", + } + ) while True: user_input = input("Enter a message: ") if user_input == "q": @@ -44,9 +50,9 @@ async def main() -> None: ) await connection.response.create() async for event in connection: - if event.type == "response.text.delta": + if event.type == "response.output_text.delta": print(event.delta, flush=True, end="") - elif event.type == "response.text.done": + elif event.type == "response.output_text.done": print() elif event.type == "response.done": break diff --git a/examples/realtime/push_to_talk_app.py b/examples/realtime/push_to_talk_app.py index 02d3f762d0..acf38995b2 100755 --- a/examples/realtime/push_to_talk_app.py +++ b/examples/realtime/push_to_talk_app.py @@ -38,8 +38,8 @@ from textual.containers import Container from openai import AsyncOpenAI -from openai.types.beta.realtime.session import Session -from openai.resources.beta.realtime.realtime import AsyncRealtimeConnection +from openai.types.realtime.session import Session +from openai.resources.realtime.realtime import AsyncRealtimeConnection class SessionDisplay(Static): @@ -154,13 +154,21 @@ async def on_mount(self) -> None: self.run_worker(self.send_mic_audio()) async def handle_realtime_connection(self) -> None: - async with self.client.beta.realtime.connect(model="gpt-4o-realtime-preview") as conn: + async with self.client.realtime.connect(model="gpt-realtime") as conn: self.connection = conn self.connected.set() # note: this is the default and can be omitted # if you want to manually handle VAD yourself, then set `'turn_detection': None` - await conn.session.update(session={"turn_detection": {"type": "server_vad"}}) + await conn.session.update( + session={ + "audio": { + "input": {"turn_detection": {"type": "server_vad"}}, + }, + "model": "gpt-realtime", + "type": "realtime", + } + ) acc_items: dict[str, Any] = {} @@ -176,7 +184,7 @@ async def handle_realtime_connection(self) -> None: self.session = event.session continue - if event.type == "response.audio.delta": + if event.type == "response.output_audio.delta": if event.item_id != self.last_audio_item_id: self.audio_player.reset_frame_count() self.last_audio_item_id = event.item_id @@ -185,7 +193,7 @@ async def handle_realtime_connection(self) -> None: self.audio_player.add_data(bytes_data) continue - if event.type == "response.audio_transcript.delta": + if event.type == "response.output_audio_transcript.delta": try: text = acc_items[event.item_id] except KeyError: diff --git a/examples/realtime/realtime.py b/examples/realtime/realtime.py new file mode 100755 index 0000000000..214961e54c --- /dev/null +++ b/examples/realtime/realtime.py @@ -0,0 +1,54 @@ +#!/usr/bin/env rye run python +import asyncio + +from openai import AsyncOpenAI + +# Azure OpenAI Realtime Docs + +# How-to: https://learn.microsoft.com/azure/ai-services/openai/how-to/realtime-audio +# Supported models and API versions: https://learn.microsoft.com/azure/ai-services/openai/how-to/realtime-audio#supported-models +# Entra ID auth: https://learn.microsoft.com/azure/ai-services/openai/how-to/managed-identity + + +async def main() -> None: + """The following example demonstrates how to configure OpenAI to use the Realtime API. + For an audio example, see push_to_talk_app.py and update the client and model parameter accordingly. + + When prompted for user input, type a message and hit enter to send it to the model. + Enter "q" to quit the conversation. + """ + + client = AsyncOpenAI() + async with client.realtime.connect( + model="gpt-realtime", + ) as connection: + await connection.session.update( + session={ + "output_modalities": ["text"], + "model": "gpt-realtime", + "type": "realtime", + } + ) + while True: + user_input = input("Enter a message: ") + if user_input == "q": + break + + await connection.conversation.item.create( + item={ + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": user_input}], + } + ) + await connection.response.create() + async for event in connection: + if event.type == "response.output_text.delta": + print(event.delta, flush=True, end="") + elif event.type == "response.output_text.done": + print() + elif event.type == "response.done": + break + + +asyncio.run(main()) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index b944fbed5e..a03b49e0c4 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -379,6 +379,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] models as models, batches as batches, uploads as uploads, + realtime as realtime, webhooks as webhooks, responses as responses, containers as containers, diff --git a/src/openai/_client.py b/src/openai/_client.py index b99db786a7..fe5ebac42a 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -45,6 +45,7 @@ models, batches, uploads, + realtime, responses, containers, embeddings, @@ -67,6 +68,7 @@ from .resources.evals.evals import Evals, AsyncEvals from .resources.moderations import Moderations, AsyncModerations from .resources.uploads.uploads import Uploads, AsyncUploads + from .resources.realtime.realtime import Realtime, AsyncRealtime from .resources.responses.responses import Responses, AsyncResponses from .resources.containers.containers import Containers, AsyncContainers from .resources.fine_tuning.fine_tuning import FineTuning, AsyncFineTuning @@ -256,6 +258,12 @@ def responses(self) -> Responses: return Responses(self) + @cached_property + def realtime(self) -> Realtime: + from .resources.realtime import Realtime + + return Realtime(self) + @cached_property def conversations(self) -> Conversations: from .resources.conversations import Conversations @@ -581,6 +589,12 @@ def responses(self) -> AsyncResponses: return AsyncResponses(self) + @cached_property + def realtime(self) -> AsyncRealtime: + from .resources.realtime import AsyncRealtime + + return AsyncRealtime(self) + @cached_property def conversations(self) -> AsyncConversations: from .resources.conversations import AsyncConversations @@ -816,6 +830,12 @@ def responses(self) -> responses.ResponsesWithRawResponse: return ResponsesWithRawResponse(self._client.responses) + @cached_property + def realtime(self) -> realtime.RealtimeWithRawResponse: + from .resources.realtime import RealtimeWithRawResponse + + return RealtimeWithRawResponse(self._client.realtime) + @cached_property def conversations(self) -> conversations.ConversationsWithRawResponse: from .resources.conversations import ConversationsWithRawResponse @@ -925,6 +945,12 @@ def responses(self) -> responses.AsyncResponsesWithRawResponse: return AsyncResponsesWithRawResponse(self._client.responses) + @cached_property + def realtime(self) -> realtime.AsyncRealtimeWithRawResponse: + from .resources.realtime import AsyncRealtimeWithRawResponse + + return AsyncRealtimeWithRawResponse(self._client.realtime) + @cached_property def conversations(self) -> conversations.AsyncConversationsWithRawResponse: from .resources.conversations import AsyncConversationsWithRawResponse @@ -1034,6 +1060,12 @@ def responses(self) -> responses.ResponsesWithStreamingResponse: return ResponsesWithStreamingResponse(self._client.responses) + @cached_property + def realtime(self) -> realtime.RealtimeWithStreamingResponse: + from .resources.realtime import RealtimeWithStreamingResponse + + return RealtimeWithStreamingResponse(self._client.realtime) + @cached_property def conversations(self) -> conversations.ConversationsWithStreamingResponse: from .resources.conversations import ConversationsWithStreamingResponse @@ -1143,6 +1175,12 @@ def responses(self) -> responses.AsyncResponsesWithStreamingResponse: return AsyncResponsesWithStreamingResponse(self._client.responses) + @cached_property + def realtime(self) -> realtime.AsyncRealtimeWithStreamingResponse: + from .resources.realtime import AsyncRealtimeWithStreamingResponse + + return AsyncRealtimeWithStreamingResponse(self._client.realtime) + @cached_property def conversations(self) -> conversations.AsyncConversationsWithStreamingResponse: from .resources.conversations import AsyncConversationsWithStreamingResponse diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index 5c8df24014..4ecc28420a 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -19,6 +19,7 @@ from .resources.evals.evals import Evals from .resources.moderations import Moderations from .resources.uploads.uploads import Uploads + from .resources.realtime.realtime import Realtime from .resources.responses.responses import Responses from .resources.containers.containers import Containers from .resources.fine_tuning.fine_tuning import FineTuning @@ -89,6 +90,12 @@ def __load__(self) -> Webhooks: return _load_client().webhooks +class RealtimeProxy(LazyProxy["Realtime"]): + @override + def __load__(self) -> Realtime: + return _load_client().realtime + + class ResponsesProxy(LazyProxy["Responses"]): @override def __load__(self) -> Responses: @@ -147,6 +154,7 @@ def __load__(self) -> Conversations: batches: Batches = BatchesProxy().__as_proxied__() uploads: Uploads = UploadsProxy().__as_proxied__() webhooks: Webhooks = WebhooksProxy().__as_proxied__() +realtime: Realtime = RealtimeProxy().__as_proxied__() responses: Responses = ResponsesProxy().__as_proxied__() embeddings: Embeddings = EmbeddingsProxy().__as_proxied__() containers: Containers = ContainersProxy().__as_proxied__() diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 6251cfed4e..64ce5eec49 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -50,7 +50,9 @@ def create( *, input: str, model: Union[str, SpeechModel], - voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]], + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] + ], instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, @@ -144,7 +146,9 @@ async def create( *, input: str, model: Union[str, SpeechModel], - voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]], + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] + ], instructions: str | NotGiven = NOT_GIVEN, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, speed: float | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 4feaaab44b..9084c477e9 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -24,10 +24,6 @@ from .realtime.realtime import ( Realtime, AsyncRealtime, - RealtimeWithRawResponse, - AsyncRealtimeWithRawResponse, - RealtimeWithStreamingResponse, - AsyncRealtimeWithStreamingResponse, ) __all__ = ["Beta", "AsyncBeta"] @@ -111,10 +107,6 @@ class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta - @cached_property - def realtime(self) -> RealtimeWithRawResponse: - return RealtimeWithRawResponse(self._beta.realtime) - @cached_property def assistants(self) -> AssistantsWithRawResponse: return AssistantsWithRawResponse(self._beta.assistants) @@ -128,10 +120,6 @@ class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta - @cached_property - def realtime(self) -> AsyncRealtimeWithRawResponse: - return AsyncRealtimeWithRawResponse(self._beta.realtime) - @cached_property def assistants(self) -> AsyncAssistantsWithRawResponse: return AsyncAssistantsWithRawResponse(self._beta.assistants) @@ -145,10 +133,6 @@ class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta - @cached_property - def realtime(self) -> RealtimeWithStreamingResponse: - return RealtimeWithStreamingResponse(self._beta.realtime) - @cached_property def assistants(self) -> AssistantsWithStreamingResponse: return AssistantsWithStreamingResponse(self._beta.assistants) @@ -162,10 +146,6 @@ class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta - @cached_property - def realtime(self) -> AsyncRealtimeWithStreamingResponse: - return AsyncRealtimeWithStreamingResponse(self._beta.realtime) - @cached_property def assistants(self) -> AsyncAssistantsWithStreamingResponse: return AsyncAssistantsWithStreamingResponse(self._beta.assistants) diff --git a/src/openai/resources/realtime/__init__.py b/src/openai/resources/realtime/__init__.py new file mode 100644 index 0000000000..7a41de8648 --- /dev/null +++ b/src/openai/resources/realtime/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .realtime import ( + Realtime, + AsyncRealtime, + RealtimeWithRawResponse, + AsyncRealtimeWithRawResponse, + RealtimeWithStreamingResponse, + AsyncRealtimeWithStreamingResponse, +) +from .client_secrets import ( + ClientSecrets, + AsyncClientSecrets, + ClientSecretsWithRawResponse, + AsyncClientSecretsWithRawResponse, + ClientSecretsWithStreamingResponse, + AsyncClientSecretsWithStreamingResponse, +) + +__all__ = [ + "ClientSecrets", + "AsyncClientSecrets", + "ClientSecretsWithRawResponse", + "AsyncClientSecretsWithRawResponse", + "ClientSecretsWithStreamingResponse", + "AsyncClientSecretsWithStreamingResponse", + "Realtime", + "AsyncRealtime", + "RealtimeWithRawResponse", + "AsyncRealtimeWithRawResponse", + "RealtimeWithStreamingResponse", + "AsyncRealtimeWithStreamingResponse", +] diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py new file mode 100644 index 0000000000..ba0f9ee538 --- /dev/null +++ b/src/openai/resources/realtime/client_secrets.py @@ -0,0 +1,185 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ... import _legacy_response +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.realtime import client_secret_create_params +from ...types.realtime.client_secret_create_response import ClientSecretCreateResponse + +__all__ = ["ClientSecrets", "AsyncClientSecrets"] + + +class ClientSecrets(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ClientSecretsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ClientSecretsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ClientSecretsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ClientSecretsWithStreamingResponse(self) + + def create( + self, + *, + expires_after: client_secret_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + session: client_secret_create_params.Session | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ClientSecretCreateResponse: + """ + Create a Realtime session and client secret for either realtime or + transcription. + + Args: + expires_after: Configuration for the ephemeral token expiration. + + session: Session configuration to use for the client secret. Choose either a realtime + session or a transcription session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/realtime/client_secrets", + body=maybe_transform( + { + "expires_after": expires_after, + "session": session, + }, + client_secret_create_params.ClientSecretCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ClientSecretCreateResponse, + ) + + +class AsyncClientSecrets(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncClientSecretsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncClientSecretsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncClientSecretsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncClientSecretsWithStreamingResponse(self) + + async def create( + self, + *, + expires_after: client_secret_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + session: client_secret_create_params.Session | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ClientSecretCreateResponse: + """ + Create a Realtime session and client secret for either realtime or + transcription. + + Args: + expires_after: Configuration for the ephemeral token expiration. + + session: Session configuration to use for the client secret. Choose either a realtime + session or a transcription session. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/realtime/client_secrets", + body=await async_maybe_transform( + { + "expires_after": expires_after, + "session": session, + }, + client_secret_create_params.ClientSecretCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ClientSecretCreateResponse, + ) + + +class ClientSecretsWithRawResponse: + def __init__(self, client_secrets: ClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = _legacy_response.to_raw_response_wrapper( + client_secrets.create, + ) + + +class AsyncClientSecretsWithRawResponse: + def __init__(self, client_secrets: AsyncClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = _legacy_response.async_to_raw_response_wrapper( + client_secrets.create, + ) + + +class ClientSecretsWithStreamingResponse: + def __init__(self, client_secrets: ClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = to_streamed_response_wrapper( + client_secrets.create, + ) + + +class AsyncClientSecretsWithStreamingResponse: + def __init__(self, client_secrets: AsyncClientSecrets) -> None: + self._client_secrets = client_secrets + + self.create = async_to_streamed_response_wrapper( + client_secrets.create, + ) diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py new file mode 100644 index 0000000000..ebdfce86e3 --- /dev/null +++ b/src/openai/resources/realtime/realtime.py @@ -0,0 +1,1056 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Iterator, cast +from typing_extensions import AsyncIterator + +import httpx +from pydantic import BaseModel + +from ..._types import NOT_GIVEN, Query, Headers, NotGiven +from ..._utils import ( + is_azure_client, + maybe_transform, + strip_not_given, + async_maybe_transform, + is_async_azure_client, +) +from ..._compat import cached_property +from ..._models import construct_type_unchecked +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._exceptions import OpenAIError +from ..._base_client import _merge_mappings +from .client_secrets import ( + ClientSecrets, + AsyncClientSecrets, + ClientSecretsWithRawResponse, + AsyncClientSecretsWithRawResponse, + ClientSecretsWithStreamingResponse, + AsyncClientSecretsWithStreamingResponse, +) +from ...types.realtime import response_create_event_param +from ...types.websocket_connection_options import WebsocketConnectionOptions +from ...types.realtime.realtime_client_event import RealtimeClientEvent +from ...types.realtime.realtime_server_event import RealtimeServerEvent +from ...types.realtime.conversation_item_param import ConversationItemParam +from ...types.realtime.realtime_client_event_param import RealtimeClientEventParam +from ...types.realtime.realtime_session_create_request_param import RealtimeSessionCreateRequestParam +from ...types.realtime.realtime_transcription_session_create_request_param import ( + RealtimeTranscriptionSessionCreateRequestParam, +) + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebsocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebsocketConnection + + from ..._client import OpenAI, AsyncOpenAI + +__all__ = ["Realtime", "AsyncRealtime"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Realtime(SyncAPIResource): + @cached_property + def client_secrets(self) -> ClientSecrets: + return ClientSecrets(self._client) + + @cached_property + def with_raw_response(self) -> RealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RealtimeWithStreamingResponse(self) + + def connect( + self, + *, + model: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> RealtimeConnectionManager: + """ + 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. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return RealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + model=model, + ) + + +class AsyncRealtime(AsyncAPIResource): + @cached_property + def client_secrets(self) -> AsyncClientSecrets: + return AsyncClientSecrets(self._client) + + @cached_property + def with_raw_response(self) -> AsyncRealtimeWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRealtimeWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRealtimeWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRealtimeWithStreamingResponse(self) + + def connect( + self, + *, + model: str, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> AsyncRealtimeConnectionManager: + """ + 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. + + Some notable benefits of the API include: + + - Native speech-to-speech: Skipping an intermediate text format means low latency and nuanced output. + - Natural, steerable voices: The models have natural inflection and can laugh, whisper, and adhere to tone direction. + - Simultaneous multimodal output: Text is useful for moderation; faster-than-realtime audio ensures stable playback. + + The Realtime API is a stateful, event-based API that communicates over a WebSocket. + """ + return AsyncRealtimeConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + model=model, + ) + + +class RealtimeWithRawResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> ClientSecretsWithRawResponse: + return ClientSecretsWithRawResponse(self._realtime.client_secrets) + + +class AsyncRealtimeWithRawResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> AsyncClientSecretsWithRawResponse: + return AsyncClientSecretsWithRawResponse(self._realtime.client_secrets) + + +class RealtimeWithStreamingResponse: + def __init__(self, realtime: Realtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> ClientSecretsWithStreamingResponse: + return ClientSecretsWithStreamingResponse(self._realtime.client_secrets) + + +class AsyncRealtimeWithStreamingResponse: + def __init__(self, realtime: AsyncRealtime) -> None: + self._realtime = realtime + + @cached_property + def client_secrets(self) -> AsyncClientSecretsWithStreamingResponse: + return AsyncClientSecretsWithStreamingResponse(self._realtime.client_secrets) + + +class AsyncRealtimeConnection: + """Represents a live websocket connection to the Realtime API""" + + session: AsyncRealtimeSessionResource + response: AsyncRealtimeResponseResource + input_audio_buffer: AsyncRealtimeInputAudioBufferResource + conversation: AsyncRealtimeConversationResource + output_audio_buffer: AsyncRealtimeOutputAudioBufferResource + transcription_session: AsyncRealtimeTranscriptionSessionResource + + _connection: AsyncWebsocketConnection + + def __init__(self, connection: AsyncWebsocketConnection) -> None: + self._connection = connection + + self.session = AsyncRealtimeSessionResource(self) + self.response = AsyncRealtimeResponseResource(self) + self.input_audio_buffer = AsyncRealtimeInputAudioBufferResource(self) + self.conversation = AsyncRealtimeConversationResource(self) + self.output_audio_buffer = AsyncRealtimeOutputAudioBufferResource(self) + self.transcription_session = AsyncRealtimeTranscriptionSessionResource(self) + + async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield await self.recv() + except ConnectionClosedOK: + return + + async def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(await self.recv_bytes()) + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug(f"Received websocket message: %s", message) + return message + + async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, RealtimeClientEventParam)) + ) + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + +class AsyncRealtimeConnectionManager: + """ + Context manager over a `AsyncRealtimeConnection` that is returned by `realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + model: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__model = model + self.__connection: AsyncRealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + async def __aenter__(self) -> AsyncRealtimeConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.realtime.connect(...).enter() + # ... + await connection.close() + ``` + """ + try: + from websockets.asyncio.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + extra_query = self.__extra_query + auth_headers = self.__client.auth_headers + if is_async_azure_client(self.__client): + url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + self.__connection = AsyncRealtimeConnection( + await connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **auth_headers, + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __aenter__ + + def _prepare_url(self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class RealtimeConnection: + """Represents a live websocket connection to the Realtime API""" + + session: RealtimeSessionResource + response: RealtimeResponseResource + input_audio_buffer: RealtimeInputAudioBufferResource + conversation: RealtimeConversationResource + output_audio_buffer: RealtimeOutputAudioBufferResource + transcription_session: RealtimeTranscriptionSessionResource + + _connection: WebsocketConnection + + def __init__(self, connection: WebsocketConnection) -> None: + self._connection = connection + + self.session = RealtimeSessionResource(self) + self.response = RealtimeResponseResource(self) + self.input_audio_buffer = RealtimeInputAudioBufferResource(self) + self.conversation = RealtimeConversationResource(self) + self.output_audio_buffer = RealtimeOutputAudioBufferResource(self) + self.transcription_session = RealtimeTranscriptionSessionResource(self) + + def __iter__(self) -> Iterator[RealtimeServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield self.recv() + except ConnectionClosedOK: + return + + def recv(self) -> RealtimeServerEvent: + """ + Receive the next message from the connection and parses it into a `RealtimeServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(self.recv_bytes()) + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `RealtimeServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug(f"Received websocket message: %s", message) + return message + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) + ) + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> RealtimeServerEvent: + """ + Converts a raw `str` or `bytes` message into a `RealtimeServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) + ) + + +class RealtimeConnectionManager: + """ + Context manager over a `RealtimeConnection` that is returned by `realtime.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + model: str, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__model = model + self.__connection: RealtimeConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + def __enter__(self) -> RealtimeConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.realtime.connect(...).enter() + # ... + connection.close() + ``` + """ + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + extra_query = self.__extra_query + auth_headers = self.__client.auth_headers + if is_azure_client(self.__client): + url, auth_headers = self.__client._configure_realtime(self.__model, extra_query) + else: + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + "model": self.__model, + **extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + self.__connection = RealtimeConnection( + connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **auth_headers, + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __enter__ + + def _prepare_url(self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseRealtimeConnectionResource: + def __init__(self, connection: RealtimeConnection) -> None: + self._connection = connection + + +class RealtimeSessionResource(BaseRealtimeConnectionResource): + def update(self, *, session: RealtimeSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to update the session’s default configuration. + The client may send this event at any time to update any field, + except for `voice`. However, note that once a session has been + initialized with a particular `model`, it can’t be changed to + another model using `session.update`. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present are updated. To clear a field like + `instructions`, pass an empty string. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class RealtimeResponseResource(BaseRealtimeConnectionResource): + def create( + self, + *, + event_id: str | NotGiven = NOT_GIVEN, + response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + ) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions`, and `temperature`. These fields will override the Session's + configuration for this Response only. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class RealtimeInputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + + +class RealtimeConversationResource(BaseRealtimeConnectionResource): + @cached_property + def item(self) -> RealtimeConversationItemResource: + return RealtimeConversationItemResource(self._connection) + + +class RealtimeConversationItemResource(BaseRealtimeConnectionResource): + def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + def create( + self, + *, + item: ConversationItemParam, + event_id: str | NotGiven = NOT_GIVEN, + previous_item_id: str | NotGiven = NOT_GIVEN, + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + def truncate( + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) + ) + + +class RealtimeOutputAudioBufferResource(BaseRealtimeConnectionResource): + def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """**WebRTC Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) + + +class RealtimeTranscriptionSessionResource(BaseRealtimeConnectionResource): + def update( + self, *, session: RealtimeTranscriptionSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to update a transcription session.""" + self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), + ) + ) + + +class BaseAsyncRealtimeConnectionResource: + def __init__(self, connection: AsyncRealtimeConnection) -> None: + self._connection = connection + + +class AsyncRealtimeSessionResource(BaseAsyncRealtimeConnectionResource): + async def update(self, *, session: RealtimeSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to update the session’s default configuration. + The client may send this event at any time to update any field, + except for `voice`. However, note that once a session has been + initialized with a particular `model`, it can’t be changed to + another model using `session.update`. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present are updated. To clear a field like + `instructions`, pass an empty string. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "session.update", "session": session, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeResponseResource(BaseAsyncRealtimeConnectionResource): + async def create( + self, + *, + event_id: str | NotGiven = NOT_GIVEN, + response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + ) -> None: + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions`, and `temperature`. These fields will override the Session's + configuration for this Response only. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.create", "event_id": event_id, "response": response}), + ) + ) + + async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "response.cancel", "event_id": event_id, "response_id": response_id}), + ) + ) + + +class AsyncRealtimeInputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) + ) + + async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event to commit the user input audio buffer, which will create a + new user message item in the conversation. This event will produce an error + if the input audio buffer is empty. When in Server VAD mode, the client does + not need to send this event, the server will commit the audio buffer + automatically. + + Committing the input audio buffer will trigger input audio transcription + (if enabled in session configuration), but it will not create a response + from the model. The server will respond with an `input_audio_buffer.committed` + event. + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) + ) + + async def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. In Server VAD + mode, the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike made other client events, the server will + not send a confirmation response to this event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "input_audio_buffer.append", "audio": audio, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeConversationResource(BaseAsyncRealtimeConnectionResource): + @cached_property + def item(self) -> AsyncRealtimeConversationItemResource: + return AsyncRealtimeConversationItemResource(self._connection) + + +class AsyncRealtimeConversationItemResource(BaseAsyncRealtimeConnectionResource): + async def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.delete", "item_id": item_id, "event_id": event_id}), + ) + ) + + async def create( + self, + *, + item: ConversationItemParam, + event_id: str | NotGiven = NOT_GIVEN, + previous_item_id: str | NotGiven = NOT_GIVEN, + ) -> None: + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.create", + "item": item, + "event_id": event_id, + "previous_item_id": previous_item_id, + } + ), + ) + ) + + async def truncate( + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given( + { + "type": "conversation.item.truncate", + "audio_end_ms": audio_end_ms, + "content_index": content_index, + "item_id": item_id, + "event_id": event_id, + } + ), + ) + ) + + async def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "conversation.item.retrieve", "item_id": item_id, "event_id": event_id}), + ) + ) + + +class AsyncRealtimeOutputAudioBufferResource(BaseAsyncRealtimeConnectionResource): + async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + """**WebRTC Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + await self._connection.send( + cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) + ) + + +class AsyncRealtimeTranscriptionSessionResource(BaseAsyncRealtimeConnectionResource): + async def update( + self, *, session: RealtimeTranscriptionSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN + ) -> None: + """Send this event to update a transcription session.""" + await self._connection.send( + cast( + RealtimeClientEventParam, + strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), + ) + ) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index e04382a9ff..e459f55c61 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -260,7 +260,7 @@ def create( tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -268,6 +268,9 @@ def create( [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about @@ -496,7 +499,7 @@ def create( tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -504,6 +507,9 @@ def create( [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about @@ -732,7 +738,7 @@ def create( tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -740,6 +746,9 @@ def create( [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about @@ -1682,7 +1691,7 @@ async def create( tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -1690,6 +1699,9 @@ async def create( [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about @@ -1918,7 +1930,7 @@ async def create( tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -1926,6 +1938,9 @@ async def create( [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about @@ -2154,7 +2169,7 @@ async def create( tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -2162,6 +2177,9 @@ async def create( [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index feeb68c68b..634d788191 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -20,7 +20,9 @@ class SpeechCreateParams(TypedDict, total=False): `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. """ - voice: Required[Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]]] + voice: Required[ + Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + ] """The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index dc68159c1e..b1576b41df 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -15,7 +15,9 @@ class ChatCompletionAudioParam(TypedDict, total=False): Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. """ - voice: Required[Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse"]]] + voice: Required[ + Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + ] """The voice the model uses to respond. Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, diff --git a/src/openai/types/realtime/__init__.py b/src/openai/types/realtime/__init__.py new file mode 100644 index 0000000000..b05f620619 --- /dev/null +++ b/src/openai/types/realtime/__init__.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .realtime_error import RealtimeError as RealtimeError +from .realtime_session import RealtimeSession as RealtimeSession +from .conversation_item import ConversationItem as ConversationItem +from .realtime_response import RealtimeResponse as RealtimeResponse +from .log_prob_properties import LogProbProperties as LogProbProperties +from .realtime_truncation import RealtimeTruncation as RealtimeTruncation +from .response_done_event import ResponseDoneEvent as ResponseDoneEvent +from .realtime_error_event import RealtimeErrorEvent as RealtimeErrorEvent +from .session_update_event import SessionUpdateEvent as SessionUpdateEvent +from .mcp_list_tools_failed import McpListToolsFailed as McpListToolsFailed +from .realtime_audio_config import RealtimeAudioConfig as RealtimeAudioConfig +from .realtime_client_event import RealtimeClientEvent as RealtimeClientEvent +from .realtime_server_event import RealtimeServerEvent as RealtimeServerEvent +from .realtime_tools_config import RealtimeToolsConfig as RealtimeToolsConfig +from .response_cancel_event import ResponseCancelEvent as ResponseCancelEvent +from .response_create_event import ResponseCreateEvent as ResponseCreateEvent +from .session_created_event import SessionCreatedEvent as SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent +from .conversation_item_done import ConversationItemDone as ConversationItemDone +from .realtime_mcp_tool_call import RealtimeMcpToolCall as RealtimeMcpToolCall +from .realtime_mcphttp_error import RealtimeMcphttpError as RealtimeMcphttpError +from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent +from .conversation_item_added import ConversationItemAdded as ConversationItemAdded +from .conversation_item_param import ConversationItemParam as ConversationItemParam +from .realtime_connect_params import RealtimeConnectParams as RealtimeConnectParams +from .realtime_mcp_list_tools import RealtimeMcpListTools as RealtimeMcpListTools +from .realtime_response_usage import RealtimeResponseUsage as RealtimeResponseUsage +from .realtime_tracing_config import RealtimeTracingConfig as RealtimeTracingConfig +from .mcp_list_tools_completed import McpListToolsCompleted as McpListToolsCompleted +from .realtime_response_status import RealtimeResponseStatus as RealtimeResponseStatus +from .response_mcp_call_failed import ResponseMcpCallFailed as ResponseMcpCallFailed +from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .rate_limits_updated_event import RateLimitsUpdatedEvent as RateLimitsUpdatedEvent +from .realtime_truncation_param import RealtimeTruncationParam as RealtimeTruncationParam +from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent as ConversationCreatedEvent +from .mcp_list_tools_in_progress import McpListToolsInProgress as McpListToolsInProgress +from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent +from .session_update_event_param import SessionUpdateEventParam as SessionUpdateEventParam +from .client_secret_create_params import ClientSecretCreateParams as ClientSecretCreateParams +from .realtime_audio_config_param import RealtimeAudioConfigParam as RealtimeAudioConfigParam +from .realtime_client_event_param import RealtimeClientEventParam as RealtimeClientEventParam +from .realtime_mcp_protocol_error import RealtimeMcpProtocolError as RealtimeMcpProtocolError +from .realtime_tool_choice_config import RealtimeToolChoiceConfig as RealtimeToolChoiceConfig +from .realtime_tools_config_param import RealtimeToolsConfigParam as RealtimeToolsConfigParam +from .realtime_tools_config_union import RealtimeToolsConfigUnion as RealtimeToolsConfigUnion +from .response_cancel_event_param import ResponseCancelEventParam as ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam +from .response_mcp_call_completed import ResponseMcpCallCompleted as ResponseMcpCallCompleted +from .realtime_mcp_tool_call_param import RealtimeMcpToolCallParam as RealtimeMcpToolCallParam +from .realtime_mcphttp_error_param import RealtimeMcphttpErrorParam as RealtimeMcphttpErrorParam +from .transcription_session_update import TranscriptionSessionUpdate as TranscriptionSessionUpdate +from .client_secret_create_response import ClientSecretCreateResponse as ClientSecretCreateResponse +from .realtime_client_secret_config import RealtimeClientSecretConfig as RealtimeClientSecretConfig +from .realtime_mcp_approval_request import RealtimeMcpApprovalRequest as RealtimeMcpApprovalRequest +from .realtime_mcp_list_tools_param import RealtimeMcpListToolsParam as RealtimeMcpListToolsParam +from .realtime_tracing_config_param import RealtimeTracingConfigParam as RealtimeTracingConfigParam +from .response_mcp_call_in_progress import ResponseMcpCallInProgress as ResponseMcpCallInProgress +from .transcription_session_created import TranscriptionSessionCreated as TranscriptionSessionCreated +from .conversation_item_create_event import ConversationItemCreateEvent as ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent +from .realtime_mcp_approval_response import RealtimeMcpApprovalResponse as RealtimeMcpApprovalResponse +from .conversation_item_created_event import ConversationItemCreatedEvent as ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent as ConversationItemDeletedEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent as InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent as InputAudioBufferCommitEvent +from .output_audio_buffer_clear_event import OutputAudioBufferClearEvent as OutputAudioBufferClearEvent +from .realtime_session_create_request import RealtimeSessionCreateRequest as RealtimeSessionCreateRequest +from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent as ConversationItemRetrieveEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent as ConversationItemTruncateEvent +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent as InputAudioBufferClearedEvent +from .realtime_session_create_response import RealtimeSessionCreateResponse as RealtimeSessionCreateResponse +from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent +from .response_mcp_call_arguments_done import ResponseMcpCallArgumentsDone as ResponseMcpCallArgumentsDone +from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent as ConversationItemTruncatedEvent +from .realtime_mcp_protocol_error_param import RealtimeMcpProtocolErrorParam as RealtimeMcpProtocolErrorParam +from .realtime_mcp_tool_execution_error import RealtimeMcpToolExecutionError as RealtimeMcpToolExecutionError +from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam as RealtimeToolChoiceConfigParam +from .realtime_tools_config_union_param import RealtimeToolsConfigUnionParam as RealtimeToolsConfigUnionParam +from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent +from .response_mcp_call_arguments_delta import ResponseMcpCallArgumentsDelta as ResponseMcpCallArgumentsDelta +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent as InputAudioBufferCommittedEvent +from .transcription_session_update_param import TranscriptionSessionUpdateParam as TranscriptionSessionUpdateParam +from .realtime_client_secret_config_param import RealtimeClientSecretConfigParam as RealtimeClientSecretConfigParam +from .realtime_mcp_approval_request_param import RealtimeMcpApprovalRequestParam as RealtimeMcpApprovalRequestParam +from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent as TranscriptionSessionUpdatedEvent +from .conversation_item_create_event_param import ConversationItemCreateEventParam as ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam as ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam as InputAudioBufferClearEventParam +from .input_audio_buffer_timeout_triggered import InputAudioBufferTimeoutTriggered as InputAudioBufferTimeoutTriggered +from .realtime_mcp_approval_response_param import RealtimeMcpApprovalResponseParam as RealtimeMcpApprovalResponseParam +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam as InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam as InputAudioBufferCommitEventParam +from .output_audio_buffer_clear_event_param import OutputAudioBufferClearEventParam as OutputAudioBufferClearEventParam +from .realtime_session_create_request_param import ( + RealtimeSessionCreateRequestParam as RealtimeSessionCreateRequestParam, +) +from .response_audio_transcript_delta_event import ( + ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, +) +from .conversation_item_retrieve_event_param import ( + ConversationItemRetrieveEventParam as ConversationItemRetrieveEventParam, +) +from .conversation_item_truncate_event_param import ( + ConversationItemTruncateEventParam as ConversationItemTruncateEventParam, +) +from .input_audio_buffer_speech_started_event import ( + InputAudioBufferSpeechStartedEvent as InputAudioBufferSpeechStartedEvent, +) +from .input_audio_buffer_speech_stopped_event import ( + InputAudioBufferSpeechStoppedEvent as InputAudioBufferSpeechStoppedEvent, +) +from .realtime_conversation_item_user_message import ( + RealtimeConversationItemUserMessage as RealtimeConversationItemUserMessage, +) +from .realtime_mcp_tool_execution_error_param import ( + RealtimeMcpToolExecutionErrorParam as RealtimeMcpToolExecutionErrorParam, +) +from .realtime_conversation_item_function_call import ( + RealtimeConversationItemFunctionCall as RealtimeConversationItemFunctionCall, +) +from .realtime_conversation_item_system_message import ( + RealtimeConversationItemSystemMessage as RealtimeConversationItemSystemMessage, +) +from .realtime_response_usage_input_token_details import ( + RealtimeResponseUsageInputTokenDetails as RealtimeResponseUsageInputTokenDetails, +) +from .response_function_call_arguments_done_event import ( + ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, +) +from .realtime_conversation_item_assistant_message import ( + RealtimeConversationItemAssistantMessage as RealtimeConversationItemAssistantMessage, +) +from .realtime_response_usage_output_token_details import ( + RealtimeResponseUsageOutputTokenDetails as RealtimeResponseUsageOutputTokenDetails, +) +from .response_function_call_arguments_delta_event import ( + ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, +) +from .realtime_conversation_item_user_message_param import ( + RealtimeConversationItemUserMessageParam as RealtimeConversationItemUserMessageParam, +) +from .realtime_transcription_session_create_request import ( + RealtimeTranscriptionSessionCreateRequest as RealtimeTranscriptionSessionCreateRequest, +) +from .realtime_conversation_item_function_call_param import ( + RealtimeConversationItemFunctionCallParam as RealtimeConversationItemFunctionCallParam, +) +from .realtime_conversation_item_function_call_output import ( + RealtimeConversationItemFunctionCallOutput as RealtimeConversationItemFunctionCallOutput, +) +from .realtime_conversation_item_system_message_param import ( + RealtimeConversationItemSystemMessageParam as RealtimeConversationItemSystemMessageParam, +) +from .realtime_conversation_item_assistant_message_param import ( + RealtimeConversationItemAssistantMessageParam as RealtimeConversationItemAssistantMessageParam, +) +from .conversation_item_input_audio_transcription_segment import ( + ConversationItemInputAudioTranscriptionSegment as ConversationItemInputAudioTranscriptionSegment, +) +from .realtime_transcription_session_create_request_param import ( + RealtimeTranscriptionSessionCreateRequestParam as RealtimeTranscriptionSessionCreateRequestParam, +) +from .realtime_conversation_item_function_call_output_param import ( + RealtimeConversationItemFunctionCallOutputParam as RealtimeConversationItemFunctionCallOutputParam, +) +from .conversation_item_input_audio_transcription_delta_event import ( + ConversationItemInputAudioTranscriptionDeltaEvent as ConversationItemInputAudioTranscriptionDeltaEvent, +) +from .conversation_item_input_audio_transcription_failed_event import ( + ConversationItemInputAudioTranscriptionFailedEvent as ConversationItemInputAudioTranscriptionFailedEvent, +) +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent as ConversationItemInputAudioTranscriptionCompletedEvent, +) diff --git a/src/openai/types/realtime/client_secret_create_params.py b/src/openai/types/realtime/client_secret_create_params.py new file mode 100644 index 0000000000..696176e5a8 --- /dev/null +++ b/src/openai/types/realtime/client_secret_create_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias, TypedDict + +from .realtime_session_create_request_param import RealtimeSessionCreateRequestParam +from .realtime_transcription_session_create_request_param import RealtimeTranscriptionSessionCreateRequestParam + +__all__ = ["ClientSecretCreateParams", "ExpiresAfter", "Session"] + + +class ClientSecretCreateParams(TypedDict, total=False): + expires_after: ExpiresAfter + """Configuration for the ephemeral token expiration.""" + + session: Session + """Session configuration to use for the client secret. + + Choose either a realtime session or a transcription session. + """ + + +class ExpiresAfter(TypedDict, total=False): + anchor: Literal["created_at"] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: int + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +Session: TypeAlias = Union[RealtimeSessionCreateRequestParam, RealtimeTranscriptionSessionCreateRequestParam] diff --git a/src/openai/types/realtime/client_secret_create_response.py b/src/openai/types/realtime/client_secret_create_response.py new file mode 100644 index 0000000000..ea8b9f9ca1 --- /dev/null +++ b/src/openai/types/realtime/client_secret_create_response.py @@ -0,0 +1,110 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .realtime_session_create_response import RealtimeSessionCreateResponse + +__all__ = [ + "ClientSecretCreateResponse", + "Session", + "SessionRealtimeTranscriptionSessionCreateResponse", + "SessionRealtimeTranscriptionSessionCreateResponseAudio", + "SessionRealtimeTranscriptionSessionCreateResponseAudioInput", + "SessionRealtimeTranscriptionSessionCreateResponseAudioInputNoiseReduction", + "SessionRealtimeTranscriptionSessionCreateResponseAudioInputTranscription", + "SessionRealtimeTranscriptionSessionCreateResponseAudioInputTurnDetection", +] + + +class SessionRealtimeTranscriptionSessionCreateResponseAudioInputNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + + +class SessionRealtimeTranscriptionSessionCreateResponseAudioInputTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """The model to use for transcription. + + Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. + """ + + prompt: Optional[str] = None + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + + +class SessionRealtimeTranscriptionSessionCreateResponseAudioInputTurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + + silence_duration_ms: Optional[int] = None + + threshold: Optional[float] = None + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class SessionRealtimeTranscriptionSessionCreateResponseAudioInput(BaseModel): + format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + noise_reduction: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInputNoiseReduction] = None + """Configuration for input audio noise reduction.""" + + transcription: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInputTranscription] = None + """Configuration of the transcription model.""" + + turn_detection: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInputTurnDetection] = None + """Configuration for turn detection.""" + + +class SessionRealtimeTranscriptionSessionCreateResponseAudio(BaseModel): + input: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInput] = None + + +class SessionRealtimeTranscriptionSessionCreateResponse(BaseModel): + id: Optional[str] = None + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + audio: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudio] = None + """Configuration for input audio for the session.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + object: Optional[str] = None + """The object type. Always `realtime.transcription_session`.""" + + +Session: TypeAlias = Union[RealtimeSessionCreateResponse, SessionRealtimeTranscriptionSessionCreateResponse] + + +class ClientSecretCreateResponse(BaseModel): + expires_at: int + """Expiration timestamp for the client secret, in seconds since epoch.""" + + session: Session + """The session configuration for either a realtime or transcription session.""" + + value: str + """The generated client secret value.""" diff --git a/src/openai/types/realtime/conversation_created_event.py b/src/openai/types/realtime/conversation_created_event.py new file mode 100644 index 0000000000..6ec1dc8c85 --- /dev/null +++ b/src/openai/types/realtime/conversation_created_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationCreatedEvent", "Conversation"] + + +class Conversation(BaseModel): + id: Optional[str] = None + """The unique ID of the conversation.""" + + object: Optional[Literal["realtime.conversation"]] = None + """The object type, must be `realtime.conversation`.""" + + +class ConversationCreatedEvent(BaseModel): + conversation: Conversation + """The conversation resource.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["conversation.created"] + """The event type, must be `conversation.created`.""" diff --git a/src/openai/types/realtime/conversation_item.py b/src/openai/types/realtime/conversation_item.py new file mode 100644 index 0000000000..be021520a2 --- /dev/null +++ b/src/openai/types/realtime/conversation_item.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .realtime_mcp_tool_call import RealtimeMcpToolCall +from .realtime_mcp_list_tools import RealtimeMcpListTools +from .realtime_mcp_approval_request import RealtimeMcpApprovalRequest +from .realtime_mcp_approval_response import RealtimeMcpApprovalResponse +from .realtime_conversation_item_user_message import RealtimeConversationItemUserMessage +from .realtime_conversation_item_function_call import RealtimeConversationItemFunctionCall +from .realtime_conversation_item_system_message import RealtimeConversationItemSystemMessage +from .realtime_conversation_item_assistant_message import RealtimeConversationItemAssistantMessage +from .realtime_conversation_item_function_call_output import RealtimeConversationItemFunctionCallOutput + +__all__ = ["ConversationItem"] + +ConversationItem: TypeAlias = Annotated[ + Union[ + RealtimeConversationItemSystemMessage, + RealtimeConversationItemUserMessage, + RealtimeConversationItemAssistantMessage, + RealtimeConversationItemFunctionCall, + RealtimeConversationItemFunctionCallOutput, + RealtimeMcpApprovalResponse, + RealtimeMcpListTools, + RealtimeMcpToolCall, + RealtimeMcpApprovalRequest, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/realtime/conversation_item_added.py b/src/openai/types/realtime/conversation_item_added.py new file mode 100644 index 0000000000..ae9f6803e4 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_added.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemAdded"] + + +class ConversationItemAdded(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.added"] + """The event type, must be `conversation.item.added`.""" + + previous_item_id: Optional[str] = None + """The ID of the item that precedes this one, if any. + + This is used to maintain ordering when items are inserted. + """ diff --git a/src/openai/types/realtime/conversation_item_create_event.py b/src/openai/types/realtime/conversation_item_create_event.py new file mode 100644 index 0000000000..8fa2dfe08c --- /dev/null +++ b/src/openai/types/realtime/conversation_item_create_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreateEvent"] + + +class ConversationItemCreateEvent(BaseModel): + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.create"] + """The event type, must be `conversation.item.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + previous_item_id: Optional[str] = None + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If set + to `root`, the new item will be added to the beginning of the conversation. If + set to an existing ID, it allows an item to be inserted mid-conversation. If the + ID cannot be found, an error will be returned and the item will not be added. + """ diff --git a/src/openai/types/realtime/conversation_item_create_event_param.py b/src/openai/types/realtime/conversation_item_create_event_param.py new file mode 100644 index 0000000000..8530dc72cd --- /dev/null +++ b/src/openai/types/realtime/conversation_item_create_event_param.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .conversation_item_param import ConversationItemParam + +__all__ = ["ConversationItemCreateEventParam"] + + +class ConversationItemCreateEventParam(TypedDict, total=False): + item: Required[ConversationItemParam] + """A single item within a Realtime conversation.""" + + type: Required[Literal["conversation.item.create"]] + """The event type, must be `conversation.item.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + previous_item_id: str + """The ID of the preceding item after which the new item will be inserted. + + If not set, the new item will be appended to the end of the conversation. If set + to `root`, the new item will be added to the beginning of the conversation. If + set to an existing ID, it allows an item to be inserted mid-conversation. If the + ID cannot be found, an error will be returned and the item will not be added. + """ diff --git a/src/openai/types/realtime/conversation_item_created_event.py b/src/openai/types/realtime/conversation_item_created_event.py new file mode 100644 index 0000000000..13f24ad31a --- /dev/null +++ b/src/openai/types/realtime/conversation_item_created_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemCreatedEvent"] + + +class ConversationItemCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.created"] + """The event type, must be `conversation.item.created`.""" + + previous_item_id: Optional[str] = None + """ + The ID of the preceding item in the Conversation context, allows the client to + understand the order of the conversation. Can be `null` if the item has no + predecessor. + """ diff --git a/src/openai/types/realtime/conversation_item_delete_event.py b/src/openai/types/realtime/conversation_item_delete_event.py new file mode 100644 index 0000000000..3734f72e9d --- /dev/null +++ b/src/openai/types/realtime/conversation_item_delete_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemDeleteEvent"] + + +class ConversationItemDeleteEvent(BaseModel): + item_id: str + """The ID of the item to delete.""" + + type: Literal["conversation.item.delete"] + """The event type, must be `conversation.item.delete`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/conversation_item_delete_event_param.py b/src/openai/types/realtime/conversation_item_delete_event_param.py new file mode 100644 index 0000000000..c3f88d6627 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_delete_event_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemDeleteEventParam"] + + +class ConversationItemDeleteEventParam(TypedDict, total=False): + item_id: Required[str] + """The ID of the item to delete.""" + + type: Required[Literal["conversation.item.delete"]] + """The event type, must be `conversation.item.delete`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/conversation_item_deleted_event.py b/src/openai/types/realtime/conversation_item_deleted_event.py new file mode 100644 index 0000000000..cfe6fe85fc --- /dev/null +++ b/src/openai/types/realtime/conversation_item_deleted_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemDeletedEvent"] + + +class ConversationItemDeletedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item that was deleted.""" + + type: Literal["conversation.item.deleted"] + """The event type, must be `conversation.item.deleted`.""" diff --git a/src/openai/types/realtime/conversation_item_done.py b/src/openai/types/realtime/conversation_item_done.py new file mode 100644 index 0000000000..a4c9b8a840 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_done.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ConversationItemDone"] + + +class ConversationItemDone(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.done"] + """The event type, must be `conversation.item.done`.""" + + previous_item_id: Optional[str] = None + """The ID of the item that precedes this one, if any. + + This is used to maintain ordering when items are inserted. + """ diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py new file mode 100644 index 0000000000..eda3f3bab6 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .log_prob_properties import LogProbProperties + +__all__ = [ + "ConversationItemInputAudioTranscriptionCompletedEvent", + "Usage", + "UsageTranscriptTextUsageTokens", + "UsageTranscriptTextUsageTokensInputTokenDetails", + "UsageTranscriptTextUsageDuration", +] + + +class UsageTranscriptTextUsageTokensInputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class UsageTranscriptTextUsageTokens(BaseModel): + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageTranscriptTextUsageTokensInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class UsageTranscriptTextUsageDuration(BaseModel): + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +Usage: TypeAlias = Union[UsageTranscriptTextUsageTokens, UsageTranscriptTextUsageDuration] + + +class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): + content_index: int + """The index of the content part containing the audio.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item containing the audio.""" + + transcript: str + """The transcribed text.""" + + type: Literal["conversation.item.input_audio_transcription.completed"] + """ + The event type, must be `conversation.item.input_audio_transcription.completed`. + """ + + usage: Usage + """Usage statistics for the transcription.""" + + logprobs: Optional[List[LogProbProperties]] = None + """The log probabilities of the transcription.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py new file mode 100644 index 0000000000..4e9528ccb0 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .log_prob_properties import LogProbProperties + +__all__ = ["ConversationItemInputAudioTranscriptionDeltaEvent"] + + +class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + type: Literal["conversation.item.input_audio_transcription.delta"] + """The event type, must be `conversation.item.input_audio_transcription.delta`.""" + + content_index: Optional[int] = None + """The index of the content part in the item's content array.""" + + delta: Optional[str] = None + """The text delta.""" + + logprobs: Optional[List[LogProbProperties]] = None + """The log probabilities of the transcription.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py new file mode 100644 index 0000000000..edb97bbf6f --- /dev/null +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionFailedEvent", "Error"] + + +class Error(BaseModel): + code: Optional[str] = None + """Error code, if any.""" + + message: Optional[str] = None + """A human-readable error message.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class ConversationItemInputAudioTranscriptionFailedEvent(BaseModel): + content_index: int + """The index of the content part containing the audio.""" + + error: Error + """Details of the transcription error.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item.""" + + type: Literal["conversation.item.input_audio_transcription.failed"] + """The event type, must be `conversation.item.input_audio_transcription.failed`.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py new file mode 100644 index 0000000000..e2cbc9d299 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemInputAudioTranscriptionSegment"] + + +class ConversationItemInputAudioTranscriptionSegment(BaseModel): + id: str + """The segment identifier.""" + + content_index: int + """The index of the input audio content part within the item.""" + + end: float + """End time of the segment in seconds.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item containing the input audio content.""" + + speaker: str + """The detected speaker label for this segment.""" + + start: float + """Start time of the segment in seconds.""" + + text: str + """The text for this segment.""" + + type: Literal["conversation.item.input_audio_transcription.segment"] + """The event type, must be `conversation.item.input_audio_transcription.segment`.""" diff --git a/src/openai/types/realtime/conversation_item_param.py b/src/openai/types/realtime/conversation_item_param.py new file mode 100644 index 0000000000..c8b442ecad --- /dev/null +++ b/src/openai/types/realtime/conversation_item_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .realtime_mcp_tool_call_param import RealtimeMcpToolCallParam +from .realtime_mcp_list_tools_param import RealtimeMcpListToolsParam +from .realtime_mcp_approval_request_param import RealtimeMcpApprovalRequestParam +from .realtime_mcp_approval_response_param import RealtimeMcpApprovalResponseParam +from .realtime_conversation_item_user_message_param import RealtimeConversationItemUserMessageParam +from .realtime_conversation_item_function_call_param import RealtimeConversationItemFunctionCallParam +from .realtime_conversation_item_system_message_param import RealtimeConversationItemSystemMessageParam +from .realtime_conversation_item_assistant_message_param import RealtimeConversationItemAssistantMessageParam +from .realtime_conversation_item_function_call_output_param import RealtimeConversationItemFunctionCallOutputParam + +__all__ = ["ConversationItemParam"] + +ConversationItemParam: TypeAlias = Union[ + RealtimeConversationItemSystemMessageParam, + RealtimeConversationItemUserMessageParam, + RealtimeConversationItemAssistantMessageParam, + RealtimeConversationItemFunctionCallParam, + RealtimeConversationItemFunctionCallOutputParam, + RealtimeMcpApprovalResponseParam, + RealtimeMcpListToolsParam, + RealtimeMcpToolCallParam, + RealtimeMcpApprovalRequestParam, +] diff --git a/src/openai/types/realtime/conversation_item_retrieve_event.py b/src/openai/types/realtime/conversation_item_retrieve_event.py new file mode 100644 index 0000000000..018c2ccc59 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_retrieve_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemRetrieveEvent"] + + +class ConversationItemRetrieveEvent(BaseModel): + item_id: str + """The ID of the item to retrieve.""" + + type: Literal["conversation.item.retrieve"] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/conversation_item_retrieve_event_param.py b/src/openai/types/realtime/conversation_item_retrieve_event_param.py new file mode 100644 index 0000000000..71b3ffa499 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_retrieve_event_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemRetrieveEventParam"] + + +class ConversationItemRetrieveEventParam(TypedDict, total=False): + item_id: Required[str] + """The ID of the item to retrieve.""" + + type: Required[Literal["conversation.item.retrieve"]] + """The event type, must be `conversation.item.retrieve`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/conversation_item_truncate_event.py b/src/openai/types/realtime/conversation_item_truncate_event.py new file mode 100644 index 0000000000..63b591bfdb --- /dev/null +++ b/src/openai/types/realtime/conversation_item_truncate_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemTruncateEvent"] + + +class ConversationItemTruncateEvent(BaseModel): + audio_end_ms: int + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: int + """The index of the content part to truncate. Set this to 0.""" + + item_id: str + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Literal["conversation.item.truncate"] + """The event type, must be `conversation.item.truncate`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/conversation_item_truncate_event_param.py b/src/openai/types/realtime/conversation_item_truncate_event_param.py new file mode 100644 index 0000000000..d3ad1e1e25 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_truncate_event_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ConversationItemTruncateEventParam"] + + +class ConversationItemTruncateEventParam(TypedDict, total=False): + audio_end_ms: Required[int] + """Inclusive duration up to which audio is truncated, in milliseconds. + + If the audio_end_ms is greater than the actual audio duration, the server will + respond with an error. + """ + + content_index: Required[int] + """The index of the content part to truncate. Set this to 0.""" + + item_id: Required[str] + """The ID of the assistant message item to truncate. + + Only assistant message items can be truncated. + """ + + type: Required[Literal["conversation.item.truncate"]] + """The event type, must be `conversation.item.truncate`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/conversation_item_truncated_event.py b/src/openai/types/realtime/conversation_item_truncated_event.py new file mode 100644 index 0000000000..f56cabc3d9 --- /dev/null +++ b/src/openai/types/realtime/conversation_item_truncated_event.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ConversationItemTruncatedEvent"] + + +class ConversationItemTruncatedEvent(BaseModel): + audio_end_ms: int + """The duration up to which the audio was truncated, in milliseconds.""" + + content_index: int + """The index of the content part that was truncated.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the assistant message item that was truncated.""" + + type: Literal["conversation.item.truncated"] + """The event type, must be `conversation.item.truncated`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_append_event.py b/src/openai/types/realtime/input_audio_buffer_append_event.py new file mode 100644 index 0000000000..8562cf0af4 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_append_event.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferAppendEvent"] + + +class InputAudioBufferAppendEvent(BaseModel): + audio: str + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Literal["input_audio_buffer.append"] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_append_event_param.py b/src/openai/types/realtime/input_audio_buffer_append_event_param.py new file mode 100644 index 0000000000..3ad0bc737d --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_append_event_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferAppendEventParam"] + + +class InputAudioBufferAppendEventParam(TypedDict, total=False): + audio: Required[str] + """Base64-encoded audio bytes. + + This must be in the format specified by the `input_audio_format` field in the + session configuration. + """ + + type: Required[Literal["input_audio_buffer.append"]] + """The event type, must be `input_audio_buffer.append`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_clear_event.py b/src/openai/types/realtime/input_audio_buffer_clear_event.py new file mode 100644 index 0000000000..9922ff3b32 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_clear_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferClearEvent"] + + +class InputAudioBufferClearEvent(BaseModel): + type: Literal["input_audio_buffer.clear"] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_clear_event_param.py b/src/openai/types/realtime/input_audio_buffer_clear_event_param.py new file mode 100644 index 0000000000..2bd6bc5a02 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_clear_event_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferClearEventParam"] + + +class InputAudioBufferClearEventParam(TypedDict, total=False): + type: Required[Literal["input_audio_buffer.clear"]] + """The event type, must be `input_audio_buffer.clear`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_cleared_event.py b/src/openai/types/realtime/input_audio_buffer_cleared_event.py new file mode 100644 index 0000000000..af71844f2f --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_cleared_event.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferClearedEvent"] + + +class InputAudioBufferClearedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + type: Literal["input_audio_buffer.cleared"] + """The event type, must be `input_audio_buffer.cleared`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_commit_event.py b/src/openai/types/realtime/input_audio_buffer_commit_event.py new file mode 100644 index 0000000000..125c3ba1e8 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_commit_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferCommitEvent"] + + +class InputAudioBufferCommitEvent(BaseModel): + type: Literal["input_audio_buffer.commit"] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_commit_event_param.py b/src/openai/types/realtime/input_audio_buffer_commit_event_param.py new file mode 100644 index 0000000000..c9c927ab98 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_commit_event_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InputAudioBufferCommitEventParam"] + + +class InputAudioBufferCommitEventParam(TypedDict, total=False): + type: Required[Literal["input_audio_buffer.commit"]] + """The event type, must be `input_audio_buffer.commit`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_committed_event.py b/src/openai/types/realtime/input_audio_buffer_committed_event.py new file mode 100644 index 0000000000..5ed1b4ccc7 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_committed_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferCommittedEvent"] + + +class InputAudioBufferCommittedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + type: Literal["input_audio_buffer.committed"] + """The event type, must be `input_audio_buffer.committed`.""" + + previous_item_id: Optional[str] = None + """ + The ID of the preceding item after which the new item will be inserted. Can be + `null` if the item has no predecessor. + """ diff --git a/src/openai/types/realtime/input_audio_buffer_speech_started_event.py b/src/openai/types/realtime/input_audio_buffer_speech_started_event.py new file mode 100644 index 0000000000..865205d786 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_speech_started_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStartedEvent"] + + +class InputAudioBufferSpeechStartedEvent(BaseModel): + audio_start_ms: int + """ + Milliseconds from the start of all audio written to the buffer during the + session when speech was first detected. This will correspond to the beginning of + audio sent to the model, and thus includes the `prefix_padding_ms` configured in + the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created when speech stops.""" + + type: Literal["input_audio_buffer.speech_started"] + """The event type, must be `input_audio_buffer.speech_started`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py b/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py new file mode 100644 index 0000000000..6cb7845ff4 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferSpeechStoppedEvent"] + + +class InputAudioBufferSpeechStoppedEvent(BaseModel): + audio_end_ms: int + """Milliseconds since the session started when speech stopped. + + This will correspond to the end of audio sent to the model, and thus includes + the `min_silence_duration_ms` configured in the Session. + """ + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the user message item that will be created.""" + + type: Literal["input_audio_buffer.speech_stopped"] + """The event type, must be `input_audio_buffer.speech_stopped`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py new file mode 100644 index 0000000000..ed592ac06b --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferTimeoutTriggered"] + + +class InputAudioBufferTimeoutTriggered(BaseModel): + audio_end_ms: int + """Millisecond offset where speech ended within the buffered audio.""" + + audio_start_ms: int + """Millisecond offset where speech started within the buffered audio.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item associated with this segment.""" + + type: Literal["input_audio_buffer.timeout_triggered"] + """The event type, must be `input_audio_buffer.timeout_triggered`.""" diff --git a/src/openai/types/realtime/log_prob_properties.py b/src/openai/types/realtime/log_prob_properties.py new file mode 100644 index 0000000000..92477d67d0 --- /dev/null +++ b/src/openai/types/realtime/log_prob_properties.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["LogProbProperties"] + + +class LogProbProperties(BaseModel): + token: str + """The token that was used to generate the log probability.""" + + bytes: List[int] + """The bytes that were used to generate the log probability.""" + + logprob: float + """The log probability of the token.""" diff --git a/src/openai/types/realtime/mcp_list_tools_completed.py b/src/openai/types/realtime/mcp_list_tools_completed.py new file mode 100644 index 0000000000..941280f01a --- /dev/null +++ b/src/openai/types/realtime/mcp_list_tools_completed.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["McpListToolsCompleted"] + + +class McpListToolsCompleted(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP list tools item.""" + + type: Literal["mcp_list_tools.completed"] + """The event type, must be `mcp_list_tools.completed`.""" diff --git a/src/openai/types/realtime/mcp_list_tools_failed.py b/src/openai/types/realtime/mcp_list_tools_failed.py new file mode 100644 index 0000000000..892eda21bd --- /dev/null +++ b/src/openai/types/realtime/mcp_list_tools_failed.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["McpListToolsFailed"] + + +class McpListToolsFailed(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP list tools item.""" + + type: Literal["mcp_list_tools.failed"] + """The event type, must be `mcp_list_tools.failed`.""" diff --git a/src/openai/types/realtime/mcp_list_tools_in_progress.py b/src/openai/types/realtime/mcp_list_tools_in_progress.py new file mode 100644 index 0000000000..4254b5fd33 --- /dev/null +++ b/src/openai/types/realtime/mcp_list_tools_in_progress.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["McpListToolsInProgress"] + + +class McpListToolsInProgress(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP list tools item.""" + + type: Literal["mcp_list_tools.in_progress"] + """The event type, must be `mcp_list_tools.in_progress`.""" diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event.py b/src/openai/types/realtime/output_audio_buffer_clear_event.py new file mode 100644 index 0000000000..b4c95039f3 --- /dev/null +++ b/src/openai/types/realtime/output_audio_buffer_clear_event.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["OutputAudioBufferClearEvent"] + + +class OutputAudioBufferClearEvent(BaseModel): + type: Literal["output_audio_buffer.clear"] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: Optional[str] = None + """The unique ID of the client event used for error handling.""" diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event_param.py b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py new file mode 100644 index 0000000000..a3205ebc6c --- /dev/null +++ b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["OutputAudioBufferClearEventParam"] + + +class OutputAudioBufferClearEventParam(TypedDict, total=False): + type: Required[Literal["output_audio_buffer.clear"]] + """The event type, must be `output_audio_buffer.clear`.""" + + event_id: str + """The unique ID of the client event used for error handling.""" diff --git a/src/openai/types/realtime/rate_limits_updated_event.py b/src/openai/types/realtime/rate_limits_updated_event.py new file mode 100644 index 0000000000..048a4028a1 --- /dev/null +++ b/src/openai/types/realtime/rate_limits_updated_event.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RateLimitsUpdatedEvent", "RateLimit"] + + +class RateLimit(BaseModel): + limit: Optional[int] = None + """The maximum allowed value for the rate limit.""" + + name: Optional[Literal["requests", "tokens"]] = None + """The name of the rate limit (`requests`, `tokens`).""" + + remaining: Optional[int] = None + """The remaining value before the limit is reached.""" + + reset_seconds: Optional[float] = None + """Seconds until the rate limit resets.""" + + +class RateLimitsUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + rate_limits: List[RateLimit] + """List of rate limit information.""" + + type: Literal["rate_limits.updated"] + """The event type, must be `rate_limits.updated`.""" diff --git a/src/openai/types/realtime/realtime_audio_config.py b/src/openai/types/realtime/realtime_audio_config.py new file mode 100644 index 0000000000..7463c70038 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_config.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeAudioConfig", "Input", "InputNoiseReduction", "InputTranscription", "InputTurnDetection", "Output"] + + +class InputNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[ + Literal[ + "whisper-1", + "gpt-4o-transcribe-latest", + "gpt-4o-mini-transcribe", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + ] + ] = None + """The model to use for transcription. + + Current options are `whisper-1`, `gpt-4o-transcribe-latest`, + `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, and `gpt-4o-transcribe-diarize`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class InputTurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + idle_timeout_ms: Optional[int] = None + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" + + +class Input(BaseModel): + format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + noise_reduction: Optional[InputNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: Optional[InputTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: Optional[InputTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class Output(BaseModel): + format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ + + speed: Optional[float] = None + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. + """ + + +class RealtimeAudioConfig(BaseModel): + input: Optional[Input] = None + + output: Optional[Output] = None diff --git a/src/openai/types/realtime/realtime_audio_config_param.py b/src/openai/types/realtime/realtime_audio_config_param.py new file mode 100644 index 0000000000..9f2e12e910 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_config_param.py @@ -0,0 +1,187 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, TypedDict + +__all__ = [ + "RealtimeAudioConfigParam", + "Input", + "InputNoiseReduction", + "InputTranscription", + "InputTurnDetection", + "Output", +] + + +class InputNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Literal[ + "whisper-1", + "gpt-4o-transcribe-latest", + "gpt-4o-mini-transcribe", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + ] + """The model to use for transcription. + + Current options are `whisper-1`, `gpt-4o-transcribe-latest`, + `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, and `gpt-4o-transcribe-diarize`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class InputTurnDetection(TypedDict, total=False): + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + idle_timeout_ms: Optional[int] + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" + + +class Input(TypedDict, total=False): + format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + noise_reduction: InputNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: InputTranscription + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: InputTurnDetection + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + +class Output(TypedDict, total=False): + format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ + + speed: float + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. + """ + + +class RealtimeAudioConfigParam(TypedDict, total=False): + input: Input + + output: Output diff --git a/src/openai/types/realtime/realtime_client_event.py b/src/openai/types/realtime/realtime_client_event.py new file mode 100644 index 0000000000..8c2c95e849 --- /dev/null +++ b/src/openai/types/realtime/realtime_client_event.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .session_update_event import SessionUpdateEvent +from .response_cancel_event import ResponseCancelEvent +from .response_create_event import ResponseCreateEvent +from .transcription_session_update import TranscriptionSessionUpdate +from .conversation_item_create_event import ConversationItemCreateEvent +from .conversation_item_delete_event import ConversationItemDeleteEvent +from .input_audio_buffer_clear_event import InputAudioBufferClearEvent +from .input_audio_buffer_append_event import InputAudioBufferAppendEvent +from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent +from .output_audio_buffer_clear_event import OutputAudioBufferClearEvent +from .conversation_item_retrieve_event import ConversationItemRetrieveEvent +from .conversation_item_truncate_event import ConversationItemTruncateEvent + +__all__ = ["RealtimeClientEvent"] + +RealtimeClientEvent: TypeAlias = Annotated[ + Union[ + ConversationItemCreateEvent, + ConversationItemDeleteEvent, + ConversationItemRetrieveEvent, + ConversationItemTruncateEvent, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + OutputAudioBufferClearEvent, + InputAudioBufferCommitEvent, + ResponseCancelEvent, + ResponseCreateEvent, + SessionUpdateEvent, + TranscriptionSessionUpdate, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/realtime/realtime_client_event_param.py b/src/openai/types/realtime/realtime_client_event_param.py new file mode 100644 index 0000000000..8e042dd64b --- /dev/null +++ b/src/openai/types/realtime/realtime_client_event_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .session_update_event_param import SessionUpdateEventParam +from .response_cancel_event_param import ResponseCancelEventParam +from .response_create_event_param import ResponseCreateEventParam +from .transcription_session_update_param import TranscriptionSessionUpdateParam +from .conversation_item_create_event_param import ConversationItemCreateEventParam +from .conversation_item_delete_event_param import ConversationItemDeleteEventParam +from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam +from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam +from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam +from .output_audio_buffer_clear_event_param import OutputAudioBufferClearEventParam +from .conversation_item_retrieve_event_param import ConversationItemRetrieveEventParam +from .conversation_item_truncate_event_param import ConversationItemTruncateEventParam + +__all__ = ["RealtimeClientEventParam"] + +RealtimeClientEventParam: TypeAlias = Union[ + ConversationItemCreateEventParam, + ConversationItemDeleteEventParam, + ConversationItemRetrieveEventParam, + ConversationItemTruncateEventParam, + InputAudioBufferAppendEventParam, + InputAudioBufferClearEventParam, + OutputAudioBufferClearEventParam, + InputAudioBufferCommitEventParam, + ResponseCancelEventParam, + ResponseCreateEventParam, + SessionUpdateEventParam, + TranscriptionSessionUpdateParam, +] diff --git a/src/openai/types/realtime/realtime_client_secret_config.py b/src/openai/types/realtime/realtime_client_secret_config.py new file mode 100644 index 0000000000..29f8f57081 --- /dev/null +++ b/src/openai/types/realtime/realtime_client_secret_config.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeClientSecretConfig", "ExpiresAfter"] + + +class ExpiresAfter(BaseModel): + anchor: Literal["created_at"] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: Optional[int] = None + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class RealtimeClientSecretConfig(BaseModel): + expires_after: Optional[ExpiresAfter] = None + """Configuration for the ephemeral token expiration.""" diff --git a/src/openai/types/realtime/realtime_client_secret_config_param.py b/src/openai/types/realtime/realtime_client_secret_config_param.py new file mode 100644 index 0000000000..30a80134ee --- /dev/null +++ b/src/openai/types/realtime/realtime_client_secret_config_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeClientSecretConfigParam", "ExpiresAfter"] + + +class ExpiresAfter(TypedDict, total=False): + anchor: Required[Literal["created_at"]] + """The anchor point for the ephemeral token expiration. + + Only `created_at` is currently supported. + """ + + seconds: int + """The number of seconds from the anchor point to the expiration. + + Select a value between `10` and `7200`. + """ + + +class RealtimeClientSecretConfigParam(TypedDict, total=False): + expires_after: ExpiresAfter + """Configuration for the ephemeral token expiration.""" diff --git a/src/openai/types/realtime/realtime_connect_params.py b/src/openai/types/realtime/realtime_connect_params.py new file mode 100644 index 0000000000..76474f3de4 --- /dev/null +++ b/src/openai/types/realtime/realtime_connect_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RealtimeConnectParams"] + + +class RealtimeConnectParams(TypedDict, total=False): + model: Required[str] diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py new file mode 100644 index 0000000000..d0f37745ea --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemAssistantMessage", "Content"] + + +class Content(BaseModel): + text: Optional[str] = None + """The text content.""" + + type: Optional[Literal["text"]] = None + """The content type. Always `text` for assistant messages.""" + + +class RealtimeConversationItemAssistantMessage(BaseModel): + content: List[Content] + """The content of the message.""" + + role: Literal["assistant"] + """The role of the message sender. Always `assistant`.""" + + type: Literal["message"] + """The type of the item. Always `message`.""" + + id: Optional[str] = None + """The unique ID of the item.""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py new file mode 100644 index 0000000000..cfbd9cd2cf --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemAssistantMessageParam", "Content"] + + +class Content(TypedDict, total=False): + text: str + """The text content.""" + + type: Literal["text"] + """The content type. Always `text` for assistant messages.""" + + +class RealtimeConversationItemAssistantMessageParam(TypedDict, total=False): + content: Required[Iterable[Content]] + """The content of the message.""" + + role: Required[Literal["assistant"]] + """The role of the message sender. Always `assistant`.""" + + type: Required[Literal["message"]] + """The type of the item. Always `message`.""" + + id: str + """The unique ID of the item.""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call.py b/src/openai/types/realtime/realtime_conversation_item_function_call.py new file mode 100644 index 0000000000..ce1c6d4cb2 --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_function_call.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemFunctionCall"] + + +class RealtimeConversationItemFunctionCall(BaseModel): + arguments: str + """The arguments of the function call.""" + + name: str + """The name of the function being called.""" + + type: Literal["function_call"] + """The type of the item. Always `function_call`.""" + + id: Optional[str] = None + """The unique ID of the item.""" + + call_id: Optional[str] = None + """The ID of the function call.""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py new file mode 100644 index 0000000000..cea840fdba --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemFunctionCallOutput"] + + +class RealtimeConversationItemFunctionCallOutput(BaseModel): + call_id: str + """The ID of the function call this output is for.""" + + output: str + """The output of the function call.""" + + type: Literal["function_call_output"] + """The type of the item. Always `function_call_output`.""" + + id: Optional[str] = None + """The unique ID of the item.""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py new file mode 100644 index 0000000000..a66c587fb6 --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemFunctionCallOutputParam"] + + +class RealtimeConversationItemFunctionCallOutputParam(TypedDict, total=False): + call_id: Required[str] + """The ID of the function call this output is for.""" + + output: Required[str] + """The output of the function call.""" + + type: Required[Literal["function_call_output"]] + """The type of the item. Always `function_call_output`.""" + + id: str + """The unique ID of the item.""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py new file mode 100644 index 0000000000..a4d6fb83ab --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemFunctionCallParam"] + + +class RealtimeConversationItemFunctionCallParam(TypedDict, total=False): + arguments: Required[str] + """The arguments of the function call.""" + + name: Required[str] + """The name of the function being called.""" + + type: Required[Literal["function_call"]] + """The type of the item. Always `function_call`.""" + + id: str + """The unique ID of the item.""" + + call_id: str + """The ID of the function call.""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message.py b/src/openai/types/realtime/realtime_conversation_item_system_message.py new file mode 100644 index 0000000000..abc67f6c5f --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_system_message.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemSystemMessage", "Content"] + + +class Content(BaseModel): + text: Optional[str] = None + """The text content.""" + + type: Optional[Literal["input_text"]] = None + """The content type. Always `input_text` for system messages.""" + + +class RealtimeConversationItemSystemMessage(BaseModel): + content: List[Content] + """The content of the message.""" + + role: Literal["system"] + """The role of the message sender. Always `system`.""" + + type: Literal["message"] + """The type of the item. Always `message`.""" + + id: Optional[str] = None + """The unique ID of the item.""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message_param.py b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py new file mode 100644 index 0000000000..2a1c442738 --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemSystemMessageParam", "Content"] + + +class Content(TypedDict, total=False): + text: str + """The text content.""" + + type: Literal["input_text"] + """The content type. Always `input_text` for system messages.""" + + +class RealtimeConversationItemSystemMessageParam(TypedDict, total=False): + content: Required[Iterable[Content]] + """The content of the message.""" + + role: Required[Literal["system"]] + """The role of the message sender. Always `system`.""" + + type: Required[Literal["message"]] + """The type of the item. Always `message`.""" + + id: str + """The unique ID of the item.""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message.py b/src/openai/types/realtime/realtime_conversation_item_user_message.py new file mode 100644 index 0000000000..48a6c6ec0a --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_user_message.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeConversationItemUserMessage", "Content"] + + +class Content(BaseModel): + audio: Optional[str] = None + """Base64-encoded audio bytes (for `input_audio`).""" + + text: Optional[str] = None + """The text content (for `input_text`).""" + + transcript: Optional[str] = None + """Transcript of the audio (for `input_audio`).""" + + type: Optional[Literal["input_text", "input_audio"]] = None + """The content type (`input_text` or `input_audio`).""" + + +class RealtimeConversationItemUserMessage(BaseModel): + content: List[Content] + """The content of the message.""" + + role: Literal["user"] + """The role of the message sender. Always `user`.""" + + type: Literal["message"] + """The type of the item. Always `message`.""" + + id: Optional[str] = None + """The unique ID of the item.""" + + object: Optional[Literal["realtime.item"]] = None + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message_param.py b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py new file mode 100644 index 0000000000..cff64a66bf --- /dev/null +++ b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeConversationItemUserMessageParam", "Content"] + + +class Content(TypedDict, total=False): + audio: str + """Base64-encoded audio bytes (for `input_audio`).""" + + text: str + """The text content (for `input_text`).""" + + transcript: str + """Transcript of the audio (for `input_audio`).""" + + type: Literal["input_text", "input_audio"] + """The content type (`input_text` or `input_audio`).""" + + +class RealtimeConversationItemUserMessageParam(TypedDict, total=False): + content: Required[Iterable[Content]] + """The content of the message.""" + + role: Required[Literal["user"]] + """The role of the message sender. Always `user`.""" + + type: Required[Literal["message"]] + """The type of the item. Always `message`.""" + + id: str + """The unique ID of the item.""" + + object: Literal["realtime.item"] + """Identifier for the API object being returned - always `realtime.item`.""" + + status: Literal["completed", "incomplete", "in_progress"] + """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_error.py b/src/openai/types/realtime/realtime_error.py new file mode 100644 index 0000000000..f1017d09e4 --- /dev/null +++ b/src/openai/types/realtime/realtime_error.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeError"] + + +class RealtimeError(BaseModel): + message: str + """A human-readable error message.""" + + type: str + """The type of error (e.g., "invalid_request_error", "server_error").""" + + code: Optional[str] = None + """Error code, if any.""" + + event_id: Optional[str] = None + """The event_id of the client event that caused the error, if applicable.""" + + param: Optional[str] = None + """Parameter related to the error, if any.""" diff --git a/src/openai/types/realtime/realtime_error_event.py b/src/openai/types/realtime/realtime_error_event.py new file mode 100644 index 0000000000..8b501d6b21 --- /dev/null +++ b/src/openai/types/realtime/realtime_error_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_error import RealtimeError + +__all__ = ["RealtimeErrorEvent"] + + +class RealtimeErrorEvent(BaseModel): + error: RealtimeError + """Details of the error.""" + + event_id: str + """The unique ID of the server event.""" + + type: Literal["error"] + """The event type, must be `error`.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_request.py b/src/openai/types/realtime/realtime_mcp_approval_request.py new file mode 100644 index 0000000000..bafc8d89d4 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_approval_request.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpApprovalRequest"] + + +class RealtimeMcpApprovalRequest(BaseModel): + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_request_param.py b/src/openai/types/realtime/realtime_mcp_approval_request_param.py new file mode 100644 index 0000000000..57c21a487f --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_approval_request_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpApprovalRequestParam"] + + +class RealtimeMcpApprovalRequestParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_response.py b/src/openai/types/realtime/realtime_mcp_approval_response.py new file mode 100644 index 0000000000..2cb03bc61a --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_approval_response.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpApprovalResponse"] + + +class RealtimeMcpApprovalResponse(BaseModel): + id: str + """The unique ID of the approval response.""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_response_param.py b/src/openai/types/realtime/realtime_mcp_approval_response_param.py new file mode 100644 index 0000000000..19b6337004 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_approval_response_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpApprovalResponseParam"] + + +class RealtimeMcpApprovalResponseParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the approval response.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] + """Optional reason for the decision.""" diff --git a/src/openai/types/realtime/realtime_mcp_list_tools.py b/src/openai/types/realtime/realtime_mcp_list_tools.py new file mode 100644 index 0000000000..aeb58a1faf --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_list_tools.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpListTools", "Tool"] + + +class Tool(BaseModel): + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class RealtimeMcpListTools(BaseModel): + server_label: str + """The label of the MCP server.""" + + tools: List[Tool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + id: Optional[str] = None + """The unique ID of the list.""" diff --git a/src/openai/types/realtime/realtime_mcp_list_tools_param.py b/src/openai/types/realtime/realtime_mcp_list_tools_param.py new file mode 100644 index 0000000000..eb8605a061 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_list_tools_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpListToolsParam", "Tool"] + + +class Tool(TypedDict, total=False): + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class RealtimeMcpListToolsParam(TypedDict, total=False): + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[Tool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + id: str + """The unique ID of the list.""" diff --git a/src/openai/types/realtime/realtime_mcp_protocol_error.py b/src/openai/types/realtime/realtime_mcp_protocol_error.py new file mode 100644 index 0000000000..2e7cfdffa3 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_protocol_error.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpProtocolError"] + + +class RealtimeMcpProtocolError(BaseModel): + code: int + + message: str + + type: Literal["protocol_error"] diff --git a/src/openai/types/realtime/realtime_mcp_protocol_error_param.py b/src/openai/types/realtime/realtime_mcp_protocol_error_param.py new file mode 100644 index 0000000000..bebe3d379e --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_protocol_error_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpProtocolErrorParam"] + + +class RealtimeMcpProtocolErrorParam(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["protocol_error"]] diff --git a/src/openai/types/realtime/realtime_mcp_tool_call.py b/src/openai/types/realtime/realtime_mcp_tool_call.py new file mode 100644 index 0000000000..533175e55b --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_tool_call.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .realtime_mcphttp_error import RealtimeMcphttpError +from .realtime_mcp_protocol_error import RealtimeMcpProtocolError +from .realtime_mcp_tool_execution_error import RealtimeMcpToolExecutionError + +__all__ = ["RealtimeMcpToolCall", "Error"] + +Error: TypeAlias = Annotated[ + Union[RealtimeMcpProtocolError, RealtimeMcpToolExecutionError, RealtimeMcphttpError, None], + PropertyInfo(discriminator="type"), +] + + +class RealtimeMcpToolCall(BaseModel): + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_tool_call"] + """The type of the item. Always `mcp_tool_call`.""" + + approval_request_id: Optional[str] = None + """The ID of an associated approval request, if any.""" + + error: Optional[Error] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" diff --git a/src/openai/types/realtime/realtime_mcp_tool_call_param.py b/src/openai/types/realtime/realtime_mcp_tool_call_param.py new file mode 100644 index 0000000000..afdc9d1d17 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_tool_call_param.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .realtime_mcphttp_error_param import RealtimeMcphttpErrorParam +from .realtime_mcp_protocol_error_param import RealtimeMcpProtocolErrorParam +from .realtime_mcp_tool_execution_error_param import RealtimeMcpToolExecutionErrorParam + +__all__ = ["RealtimeMcpToolCallParam", "Error"] + +Error: TypeAlias = Union[RealtimeMcpProtocolErrorParam, RealtimeMcpToolExecutionErrorParam, RealtimeMcphttpErrorParam] + + +class RealtimeMcpToolCallParam(TypedDict, total=False): + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_tool_call"]] + """The type of the item. Always `mcp_tool_call`.""" + + approval_request_id: Optional[str] + """The ID of an associated approval request, if any.""" + + error: Optional[Error] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" diff --git a/src/openai/types/realtime/realtime_mcp_tool_execution_error.py b/src/openai/types/realtime/realtime_mcp_tool_execution_error.py new file mode 100644 index 0000000000..a2ed063129 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_tool_execution_error.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcpToolExecutionError"] + + +class RealtimeMcpToolExecutionError(BaseModel): + message: str + + type: Literal["tool_execution_error"] diff --git a/src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py b/src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py new file mode 100644 index 0000000000..619e11c305 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcp_tool_execution_error_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcpToolExecutionErrorParam"] + + +class RealtimeMcpToolExecutionErrorParam(TypedDict, total=False): + message: Required[str] + + type: Required[Literal["tool_execution_error"]] diff --git a/src/openai/types/realtime/realtime_mcphttp_error.py b/src/openai/types/realtime/realtime_mcphttp_error.py new file mode 100644 index 0000000000..53cff91e6e --- /dev/null +++ b/src/openai/types/realtime/realtime_mcphttp_error.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeMcphttpError"] + + +class RealtimeMcphttpError(BaseModel): + code: int + + message: str + + type: Literal["http_error"] diff --git a/src/openai/types/realtime/realtime_mcphttp_error_param.py b/src/openai/types/realtime/realtime_mcphttp_error_param.py new file mode 100644 index 0000000000..2b80a6f0a4 --- /dev/null +++ b/src/openai/types/realtime/realtime_mcphttp_error_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeMcphttpErrorParam"] + + +class RealtimeMcphttpErrorParam(TypedDict, total=False): + code: Required[int] + + message: Required[str] + + type: Required[Literal["http_error"]] diff --git a/src/openai/types/realtime/realtime_response.py b/src/openai/types/realtime/realtime_response.py new file mode 100644 index 0000000000..54f5999b81 --- /dev/null +++ b/src/openai/types/realtime/realtime_response.py @@ -0,0 +1,89 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from ..shared.metadata import Metadata +from .conversation_item import ConversationItem +from .realtime_response_usage import RealtimeResponseUsage +from .realtime_response_status import RealtimeResponseStatus + +__all__ = ["RealtimeResponse"] + + +class RealtimeResponse(BaseModel): + id: Optional[str] = None + """The unique ID of the response.""" + + conversation_id: Optional[str] = None + """ + Which conversation the response is added to, determined by the `conversation` + field in the `response.create` event. If `auto`, the response will be added to + the default conversation and the value of `conversation_id` will be an id like + `conv_1234`. If `none`, the response will not be added to any conversation and + the value of `conversation_id` will be `null`. If responses are being triggered + by server VAD, the response will be added to the default conversation, thus the + `conversation_id` will be an id like `conv_1234`. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls, that was used in this response. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model used to respond. + + If there are multiple modalities, the model will pick one, for example if + `modalities` is `["text", "audio"]`, the model could be responding in either + text or audio. + """ + + object: Optional[Literal["realtime.response"]] = None + """The object type, must be `realtime.response`.""" + + output: Optional[List[ConversationItem]] = None + """The list of output items generated by the response.""" + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + status: Optional[Literal["completed", "cancelled", "failed", "incomplete", "in_progress"]] = None + """ + The final status of the response (`completed`, `cancelled`, `failed`, or + `incomplete`, `in_progress`). + """ + + status_details: Optional[RealtimeResponseStatus] = None + """Additional details about the status.""" + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + usage: Optional[RealtimeResponseUsage] = None + """Usage statistics for the Response, this will correspond to billing. + + A Realtime API session will maintain a conversation context and append new Items + to the Conversation, thus output from previous turns (text and audio tokens) + will become the input for later turns. + """ + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """ + The voice the model used to respond. Current voice options are `alloy`, `ash`, + `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ diff --git a/src/openai/types/realtime/realtime_response_status.py b/src/openai/types/realtime/realtime_response_status.py new file mode 100644 index 0000000000..12999f61a1 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_status.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeResponseStatus", "Error"] + + +class Error(BaseModel): + code: Optional[str] = None + """Error code, if any.""" + + type: Optional[str] = None + """The type of error.""" + + +class RealtimeResponseStatus(BaseModel): + error: Optional[Error] = None + """ + A description of the error that caused the response to fail, populated when the + `status` is `failed`. + """ + + reason: Optional[Literal["turn_detected", "client_cancelled", "max_output_tokens", "content_filter"]] = None + """The reason the Response did not complete. + + For a `cancelled` Response, one of `turn_detected` (the server VAD detected a + new start of speech) or `client_cancelled` (the client sent a cancel event). For + an `incomplete` Response, one of `max_output_tokens` or `content_filter` (the + server-side safety filter activated and cut off the response). + """ + + type: Optional[Literal["completed", "cancelled", "incomplete", "failed"]] = None + """ + The type of error that caused the response to fail, corresponding with the + `status` field (`completed`, `cancelled`, `incomplete`, `failed`). + """ diff --git a/src/openai/types/realtime/realtime_response_usage.py b/src/openai/types/realtime/realtime_response_usage.py new file mode 100644 index 0000000000..dbce5f28c3 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_usage.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .realtime_response_usage_input_token_details import RealtimeResponseUsageInputTokenDetails +from .realtime_response_usage_output_token_details import RealtimeResponseUsageOutputTokenDetails + +__all__ = ["RealtimeResponseUsage"] + + +class RealtimeResponseUsage(BaseModel): + input_token_details: Optional[RealtimeResponseUsageInputTokenDetails] = None + """Details about the input tokens used in the Response.""" + + input_tokens: Optional[int] = None + """ + The number of input tokens used in the Response, including text and audio + tokens. + """ + + output_token_details: Optional[RealtimeResponseUsageOutputTokenDetails] = None + """Details about the output tokens used in the Response.""" + + output_tokens: Optional[int] = None + """ + The number of output tokens sent in the Response, including text and audio + tokens. + """ + + total_tokens: Optional[int] = None + """ + The total number of tokens in the Response including input and output text and + audio tokens. + """ diff --git a/src/openai/types/realtime/realtime_response_usage_input_token_details.py b/src/openai/types/realtime/realtime_response_usage_input_token_details.py new file mode 100644 index 0000000000..dfeead90ef --- /dev/null +++ b/src/openai/types/realtime/realtime_response_usage_input_token_details.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeResponseUsageInputTokenDetails"] + + +class RealtimeResponseUsageInputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """The number of audio tokens used in the Response.""" + + cached_tokens: Optional[int] = None + """The number of cached tokens used in the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used in the Response.""" diff --git a/src/openai/types/realtime/realtime_response_usage_output_token_details.py b/src/openai/types/realtime/realtime_response_usage_output_token_details.py new file mode 100644 index 0000000000..dfa97a1f47 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_usage_output_token_details.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeResponseUsageOutputTokenDetails"] + + +class RealtimeResponseUsageOutputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """The number of audio tokens used in the Response.""" + + text_tokens: Optional[int] = None + """The number of text tokens used in the Response.""" diff --git a/src/openai/types/realtime/realtime_server_event.py b/src/openai/types/realtime/realtime_server_event.py new file mode 100644 index 0000000000..8094bcfa96 --- /dev/null +++ b/src/openai/types/realtime/realtime_server_event.py @@ -0,0 +1,159 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .conversation_item import ConversationItem +from .response_done_event import ResponseDoneEvent +from .realtime_error_event import RealtimeErrorEvent +from .mcp_list_tools_failed import McpListToolsFailed +from .session_created_event import SessionCreatedEvent +from .session_updated_event import SessionUpdatedEvent +from .conversation_item_done import ConversationItemDone +from .response_created_event import ResponseCreatedEvent +from .conversation_item_added import ConversationItemAdded +from .mcp_list_tools_completed import McpListToolsCompleted +from .response_mcp_call_failed import ResponseMcpCallFailed +from .response_text_done_event import ResponseTextDoneEvent +from .rate_limits_updated_event import RateLimitsUpdatedEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .conversation_created_event import ConversationCreatedEvent +from .mcp_list_tools_in_progress import McpListToolsInProgress +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .response_mcp_call_completed import ResponseMcpCallCompleted +from .response_mcp_call_in_progress import ResponseMcpCallInProgress +from .transcription_session_created import TranscriptionSessionCreated +from .conversation_item_created_event import ConversationItemCreatedEvent +from .conversation_item_deleted_event import ConversationItemDeletedEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .input_audio_buffer_cleared_event import InputAudioBufferClearedEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_mcp_call_arguments_done import ResponseMcpCallArgumentsDone +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .conversation_item_truncated_event import ConversationItemTruncatedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .response_mcp_call_arguments_delta import ResponseMcpCallArgumentsDelta +from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent +from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent +from .input_audio_buffer_timeout_triggered import InputAudioBufferTimeoutTriggered +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .input_audio_buffer_speech_started_event import InputAudioBufferSpeechStartedEvent +from .input_audio_buffer_speech_stopped_event import InputAudioBufferSpeechStoppedEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .conversation_item_input_audio_transcription_segment import ConversationItemInputAudioTranscriptionSegment +from .conversation_item_input_audio_transcription_delta_event import ConversationItemInputAudioTranscriptionDeltaEvent +from .conversation_item_input_audio_transcription_failed_event import ConversationItemInputAudioTranscriptionFailedEvent +from .conversation_item_input_audio_transcription_completed_event import ( + ConversationItemInputAudioTranscriptionCompletedEvent, +) + +__all__ = [ + "RealtimeServerEvent", + "ConversationItemRetrieved", + "OutputAudioBufferStarted", + "OutputAudioBufferStopped", + "OutputAudioBufferCleared", +] + + +class ConversationItemRetrieved(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + type: Literal["conversation.item.retrieved"] + """The event type, must be `conversation.item.retrieved`.""" + + +class OutputAudioBufferStarted(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.started"] + """The event type, must be `output_audio_buffer.started`.""" + + +class OutputAudioBufferStopped(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.stopped"] + """The event type, must be `output_audio_buffer.stopped`.""" + + +class OutputAudioBufferCleared(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response_id: str + """The unique ID of the response that produced the audio.""" + + type: Literal["output_audio_buffer.cleared"] + """The event type, must be `output_audio_buffer.cleared`.""" + + +RealtimeServerEvent: TypeAlias = Annotated[ + Union[ + ConversationCreatedEvent, + ConversationItemCreatedEvent, + ConversationItemDeletedEvent, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemRetrieved, + ConversationItemTruncatedEvent, + RealtimeErrorEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + RateLimitsUpdatedEvent, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + SessionCreatedEvent, + SessionUpdatedEvent, + TranscriptionSessionUpdatedEvent, + TranscriptionSessionCreated, + OutputAudioBufferStarted, + OutputAudioBufferStopped, + OutputAudioBufferCleared, + ConversationItemAdded, + ConversationItemDone, + InputAudioBufferTimeoutTriggered, + ConversationItemInputAudioTranscriptionSegment, + McpListToolsInProgress, + McpListToolsCompleted, + McpListToolsFailed, + ResponseMcpCallArgumentsDelta, + ResponseMcpCallArgumentsDone, + ResponseMcpCallInProgress, + ResponseMcpCallCompleted, + ResponseMcpCallFailed, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/realtime/realtime_session.py b/src/openai/types/realtime/realtime_session.py new file mode 100644 index 0000000000..43576ea73d --- /dev/null +++ b/src/openai/types/realtime/realtime_session.py @@ -0,0 +1,305 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..responses.response_prompt import ResponsePrompt + +__all__ = [ + "RealtimeSession", + "InputAudioNoiseReduction", + "InputAudioTranscription", + "Tool", + "Tracing", + "TracingTracingConfiguration", + "TurnDetection", +] + + +class InputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[str] = None + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class Tool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class TracingTracingConfiguration(BaseModel): + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration, None] + + +class TurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + idle_timeout_ms: Optional[int] = None + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" + + +class RealtimeSession(BaseModel): + id: Optional[str] = None + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: Optional[InputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: Optional[InputAudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_response_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + model: Optional[ + Literal[ + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ] + ] = None + """The Realtime model used for this session.""" + + object: Optional[Literal["realtime.session"]] = None + """The object type. Always `realtime.session`.""" + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is + sampled at a rate of 24kHz. + """ + + prompt: Optional[ResponsePrompt] = None + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + speed: Optional[float] = None + """The speed of the model's spoken response. + + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + """ + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[Tool]] = None + """Tools (functions) available to the model.""" + + tracing: Optional[Tracing] = None + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py new file mode 100644 index 0000000000..a8d0f99704 --- /dev/null +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -0,0 +1,116 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_truncation import RealtimeTruncation +from .realtime_audio_config import RealtimeAudioConfig +from .realtime_tools_config import RealtimeToolsConfig +from .realtime_tracing_config import RealtimeTracingConfig +from ..responses.response_prompt import ResponsePrompt +from .realtime_tool_choice_config import RealtimeToolChoiceConfig +from .realtime_client_secret_config import RealtimeClientSecretConfig + +__all__ = ["RealtimeSessionCreateRequest"] + + +class RealtimeSessionCreateRequest(BaseModel): + model: Union[ + str, + Literal[ + "gpt-4o-realtime", + "gpt-4o-mini-realtime", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + ] + """The Realtime model used for this session.""" + + type: Literal["realtime"] + """The type of session to create. Always `realtime` for the Realtime API.""" + + audio: Optional[RealtimeAudioConfig] = None + """Configuration for input and output audio.""" + + client_secret: Optional[RealtimeClientSecretConfig] = None + """Configuration options for the generated client secret.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + prompt: Optional[ResponsePrompt] = None + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ + + tool_choice: Optional[RealtimeToolChoiceConfig] = None + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Optional[RealtimeToolsConfig] = None + """Tools available to the model.""" + + tracing: Optional[RealtimeTracingConfig] = None + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + truncation: Optional[RealtimeTruncation] = None + """ + Controls how the realtime conversation is truncated prior to model inference. + The default is `auto`. When set to `retention_ratio`, the server retains a + fraction of the conversation tokens prior to the instructions. + """ diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py new file mode 100644 index 0000000000..2c5d1e0bee --- /dev/null +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -0,0 +1,119 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .realtime_truncation_param import RealtimeTruncationParam +from .realtime_audio_config_param import RealtimeAudioConfigParam +from .realtime_tools_config_param import RealtimeToolsConfigParam +from .realtime_tracing_config_param import RealtimeTracingConfigParam +from ..responses.response_prompt_param import ResponsePromptParam +from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam +from .realtime_client_secret_config_param import RealtimeClientSecretConfigParam + +__all__ = ["RealtimeSessionCreateRequestParam"] + + +class RealtimeSessionCreateRequestParam(TypedDict, total=False): + model: Required[ + Union[ + str, + Literal[ + "gpt-4o-realtime", + "gpt-4o-mini-realtime", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + ] + ] + """The Realtime model used for this session.""" + + type: Required[Literal["realtime"]] + """The type of session to create. Always `realtime` for the Realtime API.""" + + audio: RealtimeAudioConfigParam + """Configuration for input and output audio.""" + + client_secret: RealtimeClientSecretConfigParam + """Configuration options for the generated client secret.""" + + include: List[Literal["item.input_audio_transcription.logprobs"]] + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + output_modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + prompt: Optional[ResponsePromptParam] + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. + + For audio models a temperature of 0.8 is highly recommended for best + performance. + """ + + tool_choice: RealtimeToolChoiceConfigParam + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: RealtimeToolsConfigParam + """Tools available to the model.""" + + tracing: Optional[RealtimeTracingConfigParam] + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + truncation: RealtimeTruncationParam + """ + Controls how the realtime conversation is truncated prior to model inference. + The default is `auto`. When set to `retention_ratio`, the server retains a + fraction of the conversation tokens prior to the instructions. + """ diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py new file mode 100644 index 0000000000..82fa426982 --- /dev/null +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -0,0 +1,222 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = [ + "RealtimeSessionCreateResponse", + "Audio", + "AudioInput", + "AudioInputNoiseReduction", + "AudioInputTranscription", + "AudioInputTurnDetection", + "AudioOutput", + "Tool", + "Tracing", + "TracingTracingConfiguration", + "TurnDetection", +] + + +class AudioInputNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + + +class AudioInputTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio.""" + + model: Optional[str] = None + """The model to use for transcription.""" + + prompt: Optional[str] = None + """Optional text to guide the model's style or continue a previous audio segment.""" + + +class AudioInputTurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + + silence_duration_ms: Optional[int] = None + + threshold: Optional[float] = None + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class AudioInput(BaseModel): + format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + noise_reduction: Optional[AudioInputNoiseReduction] = None + """Configuration for input audio noise reduction.""" + + transcription: Optional[AudioInputTranscription] = None + """Configuration for input audio transcription.""" + + turn_detection: Optional[AudioInputTurnDetection] = None + """Configuration for turn detection.""" + + +class AudioOutput(BaseModel): + format: Optional[str] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + speed: Optional[float] = None + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + + +class Audio(BaseModel): + input: Optional[AudioInput] = None + + output: Optional[AudioOutput] = None + + +class Tool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class TracingTracingConfiguration(BaseModel): + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration] + + +class TurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class RealtimeSessionCreateResponse(BaseModel): + id: Optional[str] = None + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + audio: Optional[Audio] = None + """Configuration for input and output audio for the session.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + model: Optional[str] = None + """The Realtime model used for this session.""" + + object: Optional[str] = None + """The object type. Always `realtime.session`.""" + + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + tool_choice: Optional[str] = None + """How the model chooses tools. + + Options are `auto`, `none`, `required`, or specify a function. + """ + + tools: Optional[List[Tool]] = None + """Tools (functions) available to the model.""" + + tracing: Optional[Tracing] = None + """Configuration options for tracing. + + Set to null to disable tracing. Once tracing is enabled for a session, the + configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ diff --git a/src/openai/types/realtime/realtime_tool_choice_config.py b/src/openai/types/realtime/realtime_tool_choice_config.py new file mode 100644 index 0000000000..f93c490004 --- /dev/null +++ b/src/openai/types/realtime/realtime_tool_choice_config.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import TypeAlias + +from ..responses.tool_choice_mcp import ToolChoiceMcp +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_function import ToolChoiceFunction + +__all__ = ["RealtimeToolChoiceConfig"] + +RealtimeToolChoiceConfig: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] diff --git a/src/openai/types/realtime/realtime_tool_choice_config_param.py b/src/openai/types/realtime/realtime_tool_choice_config_param.py new file mode 100644 index 0000000000..af92f243b0 --- /dev/null +++ b/src/openai/types/realtime/realtime_tool_choice_config_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_mcp_param import ToolChoiceMcpParam +from ..responses.tool_choice_function_param import ToolChoiceFunctionParam + +__all__ = ["RealtimeToolChoiceConfigParam"] + +RealtimeToolChoiceConfigParam: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunctionParam, ToolChoiceMcpParam] diff --git a/src/openai/types/realtime/realtime_tools_config.py b/src/openai/types/realtime/realtime_tools_config.py new file mode 100644 index 0000000000..b97599ab42 --- /dev/null +++ b/src/openai/types/realtime/realtime_tools_config.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .realtime_tools_config_union import RealtimeToolsConfigUnion + +__all__ = ["RealtimeToolsConfig"] + +RealtimeToolsConfig: TypeAlias = List[RealtimeToolsConfigUnion] diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py new file mode 100644 index 0000000000..12af65c871 --- /dev/null +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -0,0 +1,158 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "RealtimeToolsConfigParam", + "RealtimeToolsConfigUnionParam", + "Function", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", +] + + +class Function(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: List[str] + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: List[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: List[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + always: McpRequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: McpRequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class Mcp(TypedDict, total=False): + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_tools: Optional[McpAllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url` or `connector_id` must be provided. + """ + + +RealtimeToolsConfigUnionParam: TypeAlias = Union[Function, Mcp] + +RealtimeToolsConfigParam: TypeAlias = List[RealtimeToolsConfigUnionParam] diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py new file mode 100644 index 0000000000..16b1557743 --- /dev/null +++ b/src/openai/types/realtime/realtime_tools_config_union.py @@ -0,0 +1,158 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "RealtimeToolsConfigUnion", + "Function", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", +] + + +class Function(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class McpAllowedToolsMcpToolFilter(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter, None] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(BaseModel): + always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[McpRequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] + + +class Mcp(BaseModel): + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_tools: Optional[McpAllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url` or `connector_id` must be provided. + """ + + +RealtimeToolsConfigUnion: TypeAlias = Annotated[Union[Function, Mcp], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py new file mode 100644 index 0000000000..1b9f18536c --- /dev/null +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -0,0 +1,155 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "RealtimeToolsConfigUnionParam", + "Function", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", +] + + +class Function(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: List[str] + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: List[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: List[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + always: McpRequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: McpRequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class Mcp(TypedDict, total=False): + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_tools: Optional[McpAllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url` or `connector_id` must be provided. + """ + + +RealtimeToolsConfigUnionParam: TypeAlias = Union[Function, Mcp] diff --git a/src/openai/types/realtime/realtime_tracing_config.py b/src/openai/types/realtime/realtime_tracing_config.py new file mode 100644 index 0000000000..1de24d6e5f --- /dev/null +++ b/src/openai/types/realtime/realtime_tracing_config.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = ["RealtimeTracingConfig", "TracingConfiguration"] + + +class TracingConfiguration(BaseModel): + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +RealtimeTracingConfig: TypeAlias = Union[Literal["auto"], TracingConfiguration, None] diff --git a/src/openai/types/realtime/realtime_tracing_config_param.py b/src/openai/types/realtime/realtime_tracing_config_param.py new file mode 100644 index 0000000000..3a35c6f7fa --- /dev/null +++ b/src/openai/types/realtime/realtime_tracing_config_param.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias, TypedDict + +__all__ = ["RealtimeTracingConfigParam", "TracingConfiguration"] + + +class TracingConfiguration(TypedDict, total=False): + group_id: str + """ + The group id to attach to this trace to enable filtering and grouping in the + traces dashboard. + """ + + metadata: object + """ + The arbitrary metadata to attach to this trace to enable filtering in the traces + dashboard. + """ + + workflow_name: str + """The name of the workflow to attach to this trace. + + This is used to name the trace in the traces dashboard. + """ + + +RealtimeTracingConfigParam: TypeAlias = Union[Literal["auto"], TracingConfiguration] diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request.py b/src/openai/types/realtime/realtime_transcription_session_create_request.py new file mode 100644 index 0000000000..d67bc92708 --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_create_request.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "RealtimeTranscriptionSessionCreateRequest", + "InputAudioNoiseReduction", + "InputAudioTranscription", + "TurnDetection", +] + + +class InputAudioNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class TurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[Literal["server_vad"]] = None + """Type of turn detection. + + Only `server_vad` is currently supported for transcription sessions. + """ + + +class RealtimeTranscriptionSessionCreateRequest(BaseModel): + model: Union[str, Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe"]] + """ID of the model to use. + + The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` + (which is powered by our open source Whisper V2 model). + """ + + type: Literal["transcription"] + """The type of session to create. + + Always `transcription` for transcription sessions. + """ + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: Optional[InputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: Optional[InputAudioTranscription] = None + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + turn_detection: Optional[TurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request_param.py b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py new file mode 100644 index 0000000000..405f0c5f2c --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypedDict + +__all__ = [ + "RealtimeTranscriptionSessionCreateRequestParam", + "InputAudioNoiseReduction", + "InputAudioTranscription", + "TurnDetection", +] + + +class InputAudioNoiseReduction(TypedDict, total=False): + type: Literal["near_field", "far_field"] + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class InputAudioTranscription(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"] + """ + The model to use for transcription, current options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ + + +class TurnDetection(TypedDict, total=False): + prefix_padding_ms: int + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: int + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: float + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Literal["server_vad"] + """Type of turn detection. + + Only `server_vad` is currently supported for transcription sessions. + """ + + +class RealtimeTranscriptionSessionCreateRequestParam(TypedDict, total=False): + model: Required[Union[str, Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe"]]] + """ID of the model to use. + + The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` + (which is powered by our open source Whisper V2 model). + """ + + type: Required[Literal["transcription"]] + """The type of session to create. + + Always `transcription` for transcription sessions. + """ + + include: List[Literal["item.input_audio_transcription.logprobs"]] + """The set of items to include in the transcription. Current available items are: + + - `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: InputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: InputAudioTranscription + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + turn_detection: TurnDetection + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ diff --git a/src/openai/types/realtime/realtime_truncation.py b/src/openai/types/realtime/realtime_truncation.py new file mode 100644 index 0000000000..4687e3da56 --- /dev/null +++ b/src/openai/types/realtime/realtime_truncation.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = ["RealtimeTruncation", "RetentionRatioTruncation"] + + +class RetentionRatioTruncation(BaseModel): + retention_ratio: float + """Fraction of pre-instruction conversation tokens to retain (0.0 - 1.0).""" + + type: Literal["retention_ratio"] + """Use retention ratio truncation.""" + + post_instructions_token_limit: Optional[int] = None + """Optional cap on tokens allowed after the instructions.""" + + +RealtimeTruncation: TypeAlias = Union[Literal["auto", "disabled"], RetentionRatioTruncation] diff --git a/src/openai/types/realtime/realtime_truncation_param.py b/src/openai/types/realtime/realtime_truncation_param.py new file mode 100644 index 0000000000..edc88ea685 --- /dev/null +++ b/src/openai/types/realtime/realtime_truncation_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["RealtimeTruncationParam", "RetentionRatioTruncation"] + + +class RetentionRatioTruncation(TypedDict, total=False): + retention_ratio: Required[float] + """Fraction of pre-instruction conversation tokens to retain (0.0 - 1.0).""" + + type: Required[Literal["retention_ratio"]] + """Use retention ratio truncation.""" + + post_instructions_token_limit: Optional[int] + """Optional cap on tokens allowed after the instructions.""" + + +RealtimeTruncationParam: TypeAlias = Union[Literal["auto", "disabled"], RetentionRatioTruncation] diff --git a/src/openai/types/realtime/response_audio_delta_event.py b/src/openai/types/realtime/response_audio_delta_event.py new file mode 100644 index 0000000000..d92c5462d0 --- /dev/null +++ b/src/openai/types/realtime/response_audio_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDeltaEvent"] + + +class ResponseAudioDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """Base64-encoded audio data delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_audio.delta"] + """The event type, must be `response.output_audio.delta`.""" diff --git a/src/openai/types/realtime/response_audio_done_event.py b/src/openai/types/realtime/response_audio_done_event.py new file mode 100644 index 0000000000..5ea0f07e36 --- /dev/null +++ b/src/openai/types/realtime/response_audio_done_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioDoneEvent"] + + +class ResponseAudioDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_audio.done"] + """The event type, must be `response.output_audio.done`.""" diff --git a/src/openai/types/realtime/response_audio_transcript_delta_event.py b/src/openai/types/realtime/response_audio_transcript_delta_event.py new file mode 100644 index 0000000000..4dd5fecac0 --- /dev/null +++ b/src/openai/types/realtime/response_audio_transcript_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDeltaEvent"] + + +class ResponseAudioTranscriptDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The transcript delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_audio_transcript.delta"] + """The event type, must be `response.output_audio_transcript.delta`.""" diff --git a/src/openai/types/realtime/response_audio_transcript_done_event.py b/src/openai/types/realtime/response_audio_transcript_done_event.py new file mode 100644 index 0000000000..2de913d277 --- /dev/null +++ b/src/openai/types/realtime/response_audio_transcript_done_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseAudioTranscriptDoneEvent"] + + +class ResponseAudioTranscriptDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + transcript: str + """The final transcript of the audio.""" + + type: Literal["response.output_audio_transcript.done"] + """The event type, must be `response.output_audio_transcript.done`.""" diff --git a/src/openai/types/realtime/response_cancel_event.py b/src/openai/types/realtime/response_cancel_event.py new file mode 100644 index 0000000000..15dc141cbf --- /dev/null +++ b/src/openai/types/realtime/response_cancel_event.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCancelEvent"] + + +class ResponseCancelEvent(BaseModel): + type: Literal["response.cancel"] + """The event type, must be `response.cancel`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response_id: Optional[str] = None + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/src/openai/types/realtime/response_cancel_event_param.py b/src/openai/types/realtime/response_cancel_event_param.py new file mode 100644 index 0000000000..f33740730a --- /dev/null +++ b/src/openai/types/realtime/response_cancel_event_param.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCancelEventParam"] + + +class ResponseCancelEventParam(TypedDict, total=False): + type: Required[Literal["response.cancel"]] + """The event type, must be `response.cancel`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response_id: str + """ + A specific response ID to cancel - if not provided, will cancel an in-progress + response in the default conversation. + """ diff --git a/src/openai/types/realtime/response_content_part_added_event.py b/src/openai/types/realtime/response_content_part_added_event.py new file mode 100644 index 0000000000..aca965c3d8 --- /dev/null +++ b/src/openai/types/realtime/response_content_part_added_event.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseContentPartAddedEvent", "Part"] + + +class Part(BaseModel): + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartAddedEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item to which the content part was added.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that was added.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.added"] + """The event type, must be `response.content_part.added`.""" diff --git a/src/openai/types/realtime/response_content_part_done_event.py b/src/openai/types/realtime/response_content_part_done_event.py new file mode 100644 index 0000000000..59af808a90 --- /dev/null +++ b/src/openai/types/realtime/response_content_part_done_event.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseContentPartDoneEvent", "Part"] + + +class Part(BaseModel): + audio: Optional[str] = None + """Base64-encoded audio data (if type is "audio").""" + + text: Optional[str] = None + """The text content (if type is "text").""" + + transcript: Optional[str] = None + """The transcript of the audio (if type is "audio").""" + + type: Optional[Literal["text", "audio"]] = None + """The content type ("text", "audio").""" + + +class ResponseContentPartDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + part: Part + """The content part that is done.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.content_part.done"] + """The event type, must be `response.content_part.done`.""" diff --git a/src/openai/types/realtime/response_create_event.py b/src/openai/types/realtime/response_create_event.py new file mode 100644 index 0000000000..a37045eab1 --- /dev/null +++ b/src/openai/types/realtime/response_create_event.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..shared.metadata import Metadata +from .conversation_item import ConversationItem +from ..responses.response_prompt import ResponsePrompt +from ..responses.tool_choice_mcp import ToolChoiceMcp +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_function import ToolChoiceFunction + +__all__ = ["ResponseCreateEvent", "Response", "ResponseToolChoice", "ResponseTool"] + +ResponseToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] + + +class ResponseTool(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" + + +class Response(BaseModel): + conversation: Union[str, Literal["auto", "none"], None] = None + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Optional[List[ConversationItem]] = None + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items from the default + conversation. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + prompt: Optional[ResponsePrompt] = None + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + temperature: Optional[float] = None + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: Optional[ResponseToolChoice] = None + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Optional[List[ResponseTool]] = None + """Tools (functions) available to the model.""" + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ + + +class ResponseCreateEvent(BaseModel): + type: Literal["response.create"] + """The event type, must be `response.create`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" + + response: Optional[Response] = None + """Create a new Realtime response with these parameters""" diff --git a/src/openai/types/realtime/response_create_event_param.py b/src/openai/types/realtime/response_create_event_param.py new file mode 100644 index 0000000000..f941c4ca9c --- /dev/null +++ b/src/openai/types/realtime/response_create_event_param.py @@ -0,0 +1,133 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..shared_params.metadata import Metadata +from .conversation_item_param import ConversationItemParam +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.response_prompt_param import ResponsePromptParam +from ..responses.tool_choice_mcp_param import ToolChoiceMcpParam +from ..responses.tool_choice_function_param import ToolChoiceFunctionParam + +__all__ = ["ResponseCreateEventParam", "Response", "ResponseToolChoice", "ResponseTool"] + +ResponseToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunctionParam, ToolChoiceMcpParam] + + +class ResponseTool(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" + + +class Response(TypedDict, total=False): + conversation: Union[str, Literal["auto", "none"]] + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Iterable[ConversationItemParam] + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items from the default + conversation. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + prompt: Optional[ResponsePromptParam] + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + temperature: float + """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" + + tool_choice: ResponseToolChoice + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Iterable[ResponseTool] + """Tools (functions) available to the model.""" + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, and `verse`. + """ + + +class ResponseCreateEventParam(TypedDict, total=False): + type: Required[Literal["response.create"]] + """The event type, must be `response.create`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" + + response: Response + """Create a new Realtime response with these parameters""" diff --git a/src/openai/types/realtime/response_created_event.py b/src/openai/types/realtime/response_created_event.py new file mode 100644 index 0000000000..996bf26f75 --- /dev/null +++ b/src/openai/types/realtime/response_created_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseCreatedEvent"] + + +class ResponseCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.created"] + """The event type, must be `response.created`.""" diff --git a/src/openai/types/realtime/response_done_event.py b/src/openai/types/realtime/response_done_event.py new file mode 100644 index 0000000000..ce9a4b9f1d --- /dev/null +++ b/src/openai/types/realtime/response_done_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_response import RealtimeResponse + +__all__ = ["ResponseDoneEvent"] + + +class ResponseDoneEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + response: RealtimeResponse + """The response resource.""" + + type: Literal["response.done"] + """The event type, must be `response.done`.""" diff --git a/src/openai/types/realtime/response_function_call_arguments_delta_event.py b/src/openai/types/realtime/response_function_call_arguments_delta_event.py new file mode 100644 index 0000000000..6d96e78b24 --- /dev/null +++ b/src/openai/types/realtime/response_function_call_arguments_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDeltaEvent"] + + +class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + call_id: str + """The ID of the function call.""" + + delta: str + """The arguments delta as a JSON string.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.delta"] + """The event type, must be `response.function_call_arguments.delta`.""" diff --git a/src/openai/types/realtime/response_function_call_arguments_done_event.py b/src/openai/types/realtime/response_function_call_arguments_done_event.py new file mode 100644 index 0000000000..be7fae9a1b --- /dev/null +++ b/src/openai/types/realtime/response_function_call_arguments_done_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionCallArgumentsDoneEvent"] + + +class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + arguments: str + """The final arguments as a JSON string.""" + + call_id: str + """The ID of the function call.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the function call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.function_call_arguments.done"] + """The event type, must be `response.function_call_arguments.done`.""" diff --git a/src/openai/types/realtime/response_mcp_call_arguments_delta.py b/src/openai/types/realtime/response_mcp_call_arguments_delta.py new file mode 100644 index 0000000000..0a02a1a578 --- /dev/null +++ b/src/openai/types/realtime/response_mcp_call_arguments_delta.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallArgumentsDelta"] + + +class ResponseMcpCallArgumentsDelta(BaseModel): + delta: str + """The JSON-encoded arguments delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.mcp_call_arguments.delta"] + """The event type, must be `response.mcp_call_arguments.delta`.""" + + obfuscation: Optional[str] = None + """If present, indicates the delta text was obfuscated.""" diff --git a/src/openai/types/realtime/response_mcp_call_arguments_done.py b/src/openai/types/realtime/response_mcp_call_arguments_done.py new file mode 100644 index 0000000000..5ec95f1728 --- /dev/null +++ b/src/openai/types/realtime/response_mcp_call_arguments_done.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallArgumentsDone"] + + +class ResponseMcpCallArgumentsDone(BaseModel): + arguments: str + """The final JSON-encoded arguments string.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.mcp_call_arguments.done"] + """The event type, must be `response.mcp_call_arguments.done`.""" diff --git a/src/openai/types/realtime/response_mcp_call_completed.py b/src/openai/types/realtime/response_mcp_call_completed.py new file mode 100644 index 0000000000..e3fcec21f0 --- /dev/null +++ b/src/openai/types/realtime/response_mcp_call_completed.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallCompleted"] + + +class ResponseMcpCallCompleted(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + type: Literal["response.mcp_call.completed"] + """The event type, must be `response.mcp_call.completed`.""" diff --git a/src/openai/types/realtime/response_mcp_call_failed.py b/src/openai/types/realtime/response_mcp_call_failed.py new file mode 100644 index 0000000000..b7adc8c2a7 --- /dev/null +++ b/src/openai/types/realtime/response_mcp_call_failed.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallFailed"] + + +class ResponseMcpCallFailed(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + type: Literal["response.mcp_call.failed"] + """The event type, must be `response.mcp_call.failed`.""" diff --git a/src/openai/types/realtime/response_mcp_call_in_progress.py b/src/openai/types/realtime/response_mcp_call_in_progress.py new file mode 100644 index 0000000000..d0fcc7615c --- /dev/null +++ b/src/openai/types/realtime/response_mcp_call_in_progress.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseMcpCallInProgress"] + + +class ResponseMcpCallInProgress(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the MCP tool call item.""" + + output_index: int + """The index of the output item in the response.""" + + type: Literal["response.mcp_call.in_progress"] + """The event type, must be `response.mcp_call.in_progress`.""" diff --git a/src/openai/types/realtime/response_output_item_added_event.py b/src/openai/types/realtime/response_output_item_added_event.py new file mode 100644 index 0000000000..509dfcaeaf --- /dev/null +++ b/src/openai/types/realtime/response_output_item_added_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemAddedEvent"] + + +class ResponseOutputItemAddedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.added"] + """The event type, must be `response.output_item.added`.""" diff --git a/src/openai/types/realtime/response_output_item_done_event.py b/src/openai/types/realtime/response_output_item_done_event.py new file mode 100644 index 0000000000..800e4ae8ee --- /dev/null +++ b/src/openai/types/realtime/response_output_item_done_event.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .conversation_item import ConversationItem + +__all__ = ["ResponseOutputItemDoneEvent"] + + +class ResponseOutputItemDoneEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + item: ConversationItem + """A single item within a Realtime conversation.""" + + output_index: int + """The index of the output item in the Response.""" + + response_id: str + """The ID of the Response to which the item belongs.""" + + type: Literal["response.output_item.done"] + """The event type, must be `response.output_item.done`.""" diff --git a/src/openai/types/realtime/response_text_delta_event.py b/src/openai/types/realtime/response_text_delta_event.py new file mode 100644 index 0000000000..493348aa22 --- /dev/null +++ b/src/openai/types/realtime/response_text_delta_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDeltaEvent"] + + +class ResponseTextDeltaEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + delta: str + """The text delta.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + type: Literal["response.output_text.delta"] + """The event type, must be `response.output_text.delta`.""" diff --git a/src/openai/types/realtime/response_text_done_event.py b/src/openai/types/realtime/response_text_done_event.py new file mode 100644 index 0000000000..83c6cf0694 --- /dev/null +++ b/src/openai/types/realtime/response_text_done_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseTextDoneEvent"] + + +class ResponseTextDoneEvent(BaseModel): + content_index: int + """The index of the content part in the item's content array.""" + + event_id: str + """The unique ID of the server event.""" + + item_id: str + """The ID of the item.""" + + output_index: int + """The index of the output item in the response.""" + + response_id: str + """The ID of the response.""" + + text: str + """The final text content.""" + + type: Literal["response.output_text.done"] + """The event type, must be `response.output_text.done`.""" diff --git a/src/openai/types/realtime/session_created_event.py b/src/openai/types/realtime/session_created_event.py new file mode 100644 index 0000000000..51f75700f0 --- /dev/null +++ b/src/openai/types/realtime/session_created_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_session import RealtimeSession + +__all__ = ["SessionCreatedEvent"] + + +class SessionCreatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: RealtimeSession + """Realtime session object.""" + + type: Literal["session.created"] + """The event type, must be `session.created`.""" diff --git a/src/openai/types/realtime/session_update_event.py b/src/openai/types/realtime/session_update_event.py new file mode 100644 index 0000000000..00a4377f96 --- /dev/null +++ b/src/openai/types/realtime/session_update_event.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_session_create_request import RealtimeSessionCreateRequest + +__all__ = ["SessionUpdateEvent"] + + +class SessionUpdateEvent(BaseModel): + session: RealtimeSessionCreateRequest + """Realtime session object configuration.""" + + type: Literal["session.update"] + """The event type, must be `session.update`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/session_update_event_param.py b/src/openai/types/realtime/session_update_event_param.py new file mode 100644 index 0000000000..79ff05f729 --- /dev/null +++ b/src/openai/types/realtime/session_update_event_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .realtime_session_create_request_param import RealtimeSessionCreateRequestParam + +__all__ = ["SessionUpdateEventParam"] + + +class SessionUpdateEventParam(TypedDict, total=False): + session: Required[RealtimeSessionCreateRequestParam] + """Realtime session object configuration.""" + + type: Required[Literal["session.update"]] + """The event type, must be `session.update`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/session_updated_event.py b/src/openai/types/realtime/session_updated_event.py new file mode 100644 index 0000000000..b8a5972f6e --- /dev/null +++ b/src/openai/types/realtime/session_updated_event.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_session import RealtimeSession + +__all__ = ["SessionUpdatedEvent"] + + +class SessionUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: RealtimeSession + """Realtime session object.""" + + type: Literal["session.updated"] + """The event type, must be `session.updated`.""" diff --git a/src/openai/types/realtime/transcription_session_created.py b/src/openai/types/realtime/transcription_session_created.py new file mode 100644 index 0000000000..1d34d152d7 --- /dev/null +++ b/src/openai/types/realtime/transcription_session_created.py @@ -0,0 +1,105 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "TranscriptionSessionCreated", + "Session", + "SessionAudio", + "SessionAudioInput", + "SessionAudioInputNoiseReduction", + "SessionAudioInputTranscription", + "SessionAudioInputTurnDetection", +] + + +class SessionAudioInputNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + + +class SessionAudioInputTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """The model to use for transcription. + + Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. + """ + + prompt: Optional[str] = None + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + + +class SessionAudioInputTurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + + silence_duration_ms: Optional[int] = None + + threshold: Optional[float] = None + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class SessionAudioInput(BaseModel): + format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + noise_reduction: Optional[SessionAudioInputNoiseReduction] = None + """Configuration for input audio noise reduction.""" + + transcription: Optional[SessionAudioInputTranscription] = None + """Configuration of the transcription model.""" + + turn_detection: Optional[SessionAudioInputTurnDetection] = None + """Configuration for turn detection.""" + + +class SessionAudio(BaseModel): + input: Optional[SessionAudioInput] = None + + +class Session(BaseModel): + id: Optional[str] = None + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + audio: Optional[SessionAudio] = None + """Configuration for input audio for the session.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + object: Optional[str] = None + """The object type. Always `realtime.transcription_session`.""" + + +class TranscriptionSessionCreated(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: Session + """A Realtime transcription session configuration object.""" + + type: Literal["transcription_session.created"] + """The event type, must be `transcription_session.created`.""" diff --git a/src/openai/types/realtime/transcription_session_update.py b/src/openai/types/realtime/transcription_session_update.py new file mode 100644 index 0000000000..c8f5b9eb4a --- /dev/null +++ b/src/openai/types/realtime/transcription_session_update.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest + +__all__ = ["TranscriptionSessionUpdate"] + + +class TranscriptionSessionUpdate(BaseModel): + session: RealtimeTranscriptionSessionCreateRequest + """Realtime transcription session object configuration.""" + + type: Literal["transcription_session.update"] + """The event type, must be `transcription_session.update`.""" + + event_id: Optional[str] = None + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/transcription_session_update_param.py b/src/openai/types/realtime/transcription_session_update_param.py new file mode 100644 index 0000000000..f2e66efaa0 --- /dev/null +++ b/src/openai/types/realtime/transcription_session_update_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .realtime_transcription_session_create_request_param import RealtimeTranscriptionSessionCreateRequestParam + +__all__ = ["TranscriptionSessionUpdateParam"] + + +class TranscriptionSessionUpdateParam(TypedDict, total=False): + session: Required[RealtimeTranscriptionSessionCreateRequestParam] + """Realtime transcription session object configuration.""" + + type: Required[Literal["transcription_session.update"]] + """The event type, must be `transcription_session.update`.""" + + event_id: str + """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/transcription_session_updated_event.py b/src/openai/types/realtime/transcription_session_updated_event.py new file mode 100644 index 0000000000..9abd1d20be --- /dev/null +++ b/src/openai/types/realtime/transcription_session_updated_event.py @@ -0,0 +1,105 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = [ + "TranscriptionSessionUpdatedEvent", + "Session", + "SessionAudio", + "SessionAudioInput", + "SessionAudioInputNoiseReduction", + "SessionAudioInputTranscription", + "SessionAudioInputTurnDetection", +] + + +class SessionAudioInputNoiseReduction(BaseModel): + type: Optional[Literal["near_field", "far_field"]] = None + + +class SessionAudioInputTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None + """The model to use for transcription. + + Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. + """ + + prompt: Optional[str] = None + """An optional text to guide the model's style or continue a previous audio + segment. + + The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + """ + + +class SessionAudioInputTurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + + silence_duration_ms: Optional[int] = None + + threshold: Optional[float] = None + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" + + +class SessionAudioInput(BaseModel): + format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + noise_reduction: Optional[SessionAudioInputNoiseReduction] = None + """Configuration for input audio noise reduction.""" + + transcription: Optional[SessionAudioInputTranscription] = None + """Configuration of the transcription model.""" + + turn_detection: Optional[SessionAudioInputTurnDetection] = None + """Configuration for turn detection.""" + + +class SessionAudio(BaseModel): + input: Optional[SessionAudioInput] = None + + +class Session(BaseModel): + id: Optional[str] = None + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + audio: Optional[SessionAudio] = None + """Configuration for input audio for the session.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + object: Optional[str] = None + """The object type. Always `realtime.transcription_session`.""" + + +class TranscriptionSessionUpdatedEvent(BaseModel): + event_id: str + """The unique ID of the server event.""" + + session: Session + """A Realtime transcription session configuration object.""" + + type: Literal["transcription_session.updated"] + """The event type, must be `transcription_session.updated`.""" diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 7c574ed315..8047f3c4d1 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -59,6 +59,7 @@ from .response_output_refusal import ResponseOutputRefusal as ResponseOutputRefusal from .response_reasoning_item import ResponseReasoningItem as ResponseReasoningItem from .tool_choice_types_param import ToolChoiceTypesParam as ToolChoiceTypesParam +from .web_search_preview_tool import WebSearchPreviewTool as WebSearchPreviewTool from .easy_input_message_param import EasyInputMessageParam as EasyInputMessageParam from .response_completed_event import ResponseCompletedEvent as ResponseCompletedEvent from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams @@ -90,6 +91,7 @@ from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam +from .web_search_preview_tool_param import WebSearchPreviewToolParam as WebSearchPreviewToolParam from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent from .response_custom_tool_call_param import ResponseCustomToolCallParam as ResponseCustomToolCallParam diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index ce9effd75e..9f6fd3e2d2 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -116,7 +116,7 @@ class Response(BaseModel): You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -124,6 +124,9 @@ class Response(BaseModel): [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index ff28c05816..eac249414a 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -216,7 +216,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): You can specify which tool to use by setting the `tool_choice` parameter. - The two categories of tools you can provide the model are: + We support the following categories of tools: - **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like @@ -224,6 +224,9 @@ class ResponseCreateParamsBase(TypedDict, total=False): [file search](https://platform.openai.com/docs/guides/tools-file-search). Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and Notion. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. Learn more about diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 0fe7133804..594e09d729 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -3,19 +3,18 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias -from . import web_search_tool from ..._utils import PropertyInfo from ..._models import BaseModel from .custom_tool import CustomTool from .computer_tool import ComputerTool from .function_tool import FunctionTool +from .web_search_tool import WebSearchTool from .file_search_tool import FileSearchTool +from .web_search_preview_tool import WebSearchPreviewTool __all__ = [ "Tool", "WebSearchTool", - "WebSearchToolFilters", - "WebSearchToolUserLocation", "Mcp", "McpAllowedTools", "McpAllowedToolsMcpToolFilter", @@ -32,61 +31,6 @@ ] -class WebSearchToolFilters(BaseModel): - allowed_domains: Optional[List[str]] = None - """Allowed domains for the search. - - If not provided, all domains are allowed. Subdomains of the provided domains are - allowed as well. - - Example: `["pubmed.ncbi.nlm.nih.gov"]` - """ - - -class WebSearchToolUserLocation(BaseModel): - city: Optional[str] = None - """Free text input for the city of the user, e.g. `San Francisco`.""" - - country: Optional[str] = None - """ - The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of - the user, e.g. `US`. - """ - - region: Optional[str] = None - """Free text input for the region of the user, e.g. `California`.""" - - timezone: Optional[str] = None - """ - The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the - user, e.g. `America/Los_Angeles`. - """ - - type: Optional[Literal["approximate"]] = None - """The type of location approximation. Always `approximate`.""" - - -class WebSearchTool(BaseModel): - type: Literal["web_search", "web_search_2025_08_26"] - """The type of the web search tool. - - One of `web_search` or `web_search_2025_08_26`. - """ - - filters: Optional[WebSearchToolFilters] = None - """Filters for the search.""" - - search_context_size: Optional[Literal["low", "medium", "high"]] = None - """High level guidance for the amount of context window space to use for the - search. - - One of `low`, `medium`, or `high`. `medium` is the default. - """ - - user_location: Optional[WebSearchToolUserLocation] = None - """The approximate location of the user.""" - - class McpAllowedToolsMcpToolFilter(BaseModel): read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -310,7 +254,7 @@ class LocalShell(BaseModel): ImageGeneration, LocalShell, CustomTool, - web_search_tool.WebSearchTool, + WebSearchPreviewTool, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index aff9359efa..def1f08094 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -11,12 +11,10 @@ from .function_tool_param import FunctionToolParam from .web_search_tool_param import WebSearchToolParam from .file_search_tool_param import FileSearchToolParam +from .web_search_preview_tool_param import WebSearchPreviewToolParam __all__ = [ "ToolParam", - "WebSearchTool", - "WebSearchToolFilters", - "WebSearchToolUserLocation", "Mcp", "McpAllowedTools", "McpAllowedToolsMcpToolFilter", @@ -33,61 +31,6 @@ ] -class WebSearchToolFilters(TypedDict, total=False): - allowed_domains: Optional[List[str]] - """Allowed domains for the search. - - If not provided, all domains are allowed. Subdomains of the provided domains are - allowed as well. - - Example: `["pubmed.ncbi.nlm.nih.gov"]` - """ - - -class WebSearchToolUserLocation(TypedDict, total=False): - city: Optional[str] - """Free text input for the city of the user, e.g. `San Francisco`.""" - - country: Optional[str] - """ - The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of - the user, e.g. `US`. - """ - - region: Optional[str] - """Free text input for the region of the user, e.g. `California`.""" - - timezone: Optional[str] - """ - The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the - user, e.g. `America/Los_Angeles`. - """ - - type: Literal["approximate"] - """The type of location approximation. Always `approximate`.""" - - -class WebSearchTool(TypedDict, total=False): - type: Required[Literal["web_search", "web_search_2025_08_26"]] - """The type of the web search tool. - - One of `web_search` or `web_search_2025_08_26`. - """ - - filters: Optional[WebSearchToolFilters] - """Filters for the search.""" - - search_context_size: Literal["low", "medium", "high"] - """High level guidance for the amount of context window space to use for the - search. - - One of `low`, `medium`, or `high`. `medium` is the default. - """ - - user_location: Optional[WebSearchToolUserLocation] - """The approximate location of the user.""" - - class McpAllowedToolsMcpToolFilter(TypedDict, total=False): read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -302,13 +245,13 @@ class LocalShell(TypedDict, total=False): FunctionToolParam, FileSearchToolParam, ComputerToolParam, - WebSearchTool, + WebSearchToolParam, Mcp, CodeInterpreter, ImageGeneration, LocalShell, CustomToolParam, - WebSearchToolParam, + WebSearchPreviewToolParam, ] diff --git a/src/openai/types/responses/web_search_preview_tool.py b/src/openai/types/responses/web_search_preview_tool.py new file mode 100644 index 0000000000..66d6a24679 --- /dev/null +++ b/src/openai/types/responses/web_search_preview_tool.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["WebSearchPreviewTool", "UserLocation"] + + +class UserLocation(BaseModel): + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchPreviewTool(BaseModel): + type: Literal["web_search_preview", "web_search_preview_2025_03_11"] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None + """The user's location.""" diff --git a/src/openai/types/responses/web_search_preview_tool_param.py b/src/openai/types/responses/web_search_preview_tool_param.py new file mode 100644 index 0000000000..ec2173f8e8 --- /dev/null +++ b/src/openai/types/responses/web_search_preview_tool_param.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["WebSearchPreviewToolParam", "UserLocation"] + + +class UserLocation(TypedDict, total=False): + type: Required[Literal["approximate"]] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class WebSearchPreviewToolParam(TypedDict, total=False): + type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] + """The user's location.""" diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py index a6bf951145..bde9600c87 100644 --- a/src/openai/types/responses/web_search_tool.py +++ b/src/openai/types/responses/web_search_tool.py @@ -1,17 +1,25 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["WebSearchTool", "UserLocation"] +__all__ = ["WebSearchTool", "Filters", "UserLocation"] -class UserLocation(BaseModel): - type: Literal["approximate"] - """The type of location approximation. Always `approximate`.""" +class Filters(BaseModel): + allowed_domains: Optional[List[str]] = None + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + +class UserLocation(BaseModel): city: Optional[str] = None """Free text input for the city of the user, e.g. `San Francisco`.""" @@ -30,14 +38,20 @@ class UserLocation(BaseModel): user, e.g. `America/Los_Angeles`. """ + type: Optional[Literal["approximate"]] = None + """The type of location approximation. Always `approximate`.""" + class WebSearchTool(BaseModel): - type: Literal["web_search_preview", "web_search_preview_2025_03_11"] + type: Literal["web_search", "web_search_2025_08_26"] """The type of the web search tool. - One of `web_search_preview` or `web_search_preview_2025_03_11`. + One of `web_search` or `web_search_2025_08_26`. """ + filters: Optional[Filters] = None + """Filters for the search.""" + search_context_size: Optional[Literal["low", "medium", "high"]] = None """High level guidance for the amount of context window space to use for the search. @@ -46,4 +60,4 @@ class WebSearchTool(BaseModel): """ user_location: Optional[UserLocation] = None - """The user's location.""" + """The approximate location of the user.""" diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py index d0335c01a3..17a382456b 100644 --- a/src/openai/types/responses/web_search_tool_param.py +++ b/src/openai/types/responses/web_search_tool_param.py @@ -2,16 +2,24 @@ from __future__ import annotations -from typing import Optional +from typing import List, Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["WebSearchToolParam", "UserLocation"] +__all__ = ["WebSearchToolParam", "Filters", "UserLocation"] -class UserLocation(TypedDict, total=False): - type: Required[Literal["approximate"]] - """The type of location approximation. Always `approximate`.""" +class Filters(TypedDict, total=False): + allowed_domains: Optional[List[str]] + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + +class UserLocation(TypedDict, total=False): city: Optional[str] """Free text input for the city of the user, e.g. `San Francisco`.""" @@ -30,14 +38,20 @@ class UserLocation(TypedDict, total=False): user, e.g. `America/Los_Angeles`. """ + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + class WebSearchToolParam(TypedDict, total=False): - type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] + type: Required[Literal["web_search", "web_search_2025_08_26"]] """The type of the web search tool. - One of `web_search_preview` or `web_search_preview_2025_03_11`. + One of `web_search` or `web_search_2025_08_26`. """ + filters: Optional[Filters] + """Filters for the search.""" + search_context_size: Literal["low", "medium", "high"] """High level guidance for the amount of context window space to use for the search. @@ -46,4 +60,4 @@ class WebSearchToolParam(TypedDict, total=False): """ user_location: Optional[UserLocation] - """The user's location.""" + """The approximate location of the user.""" diff --git a/src/openai/types/webhooks/__init__.py b/src/openai/types/webhooks/__init__.py index 9caad38c82..8b9e55653b 100644 --- a/src/openai/types/webhooks/__init__.py +++ b/src/openai/types/webhooks/__init__.py @@ -15,6 +15,7 @@ from .response_completed_webhook_event import ResponseCompletedWebhookEvent as ResponseCompletedWebhookEvent from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent as ResponseIncompleteWebhookEvent from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent as FineTuningJobFailedWebhookEvent +from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent as RealtimeCallIncomingWebhookEvent from .fine_tuning_job_cancelled_webhook_event import ( FineTuningJobCancelledWebhookEvent as FineTuningJobCancelledWebhookEvent, ) diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py new file mode 100644 index 0000000000..a166a3471b --- /dev/null +++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeCallIncomingWebhookEvent", "Data", "DataSipHeader"] + + +class DataSipHeader(BaseModel): + name: str + """Name of the SIP Header.""" + + value: str + """Value of the SIP Header.""" + + +class Data(BaseModel): + call_id: str + """The unique ID of this call.""" + + sip_headers: List[DataSipHeader] + """Headers from the SIP Invite.""" + + +class RealtimeCallIncomingWebhookEvent(BaseModel): + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the model response was completed.""" + + data: Data + """Event data payload.""" + + type: Literal["realtime.call.incoming"] + """The type of the event. Always `realtime.call.incoming`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/src/openai/types/webhooks/unwrap_webhook_event.py b/src/openai/types/webhooks/unwrap_webhook_event.py index 91091af32f..952383c049 100644 --- a/src/openai/types/webhooks/unwrap_webhook_event.py +++ b/src/openai/types/webhooks/unwrap_webhook_event.py @@ -16,6 +16,7 @@ from .response_completed_webhook_event import ResponseCompletedWebhookEvent from .response_incomplete_webhook_event import ResponseIncompleteWebhookEvent from .fine_tuning_job_failed_webhook_event import FineTuningJobFailedWebhookEvent +from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent from .fine_tuning_job_cancelled_webhook_event import FineTuningJobCancelledWebhookEvent from .fine_tuning_job_succeeded_webhook_event import FineTuningJobSucceededWebhookEvent @@ -33,6 +34,7 @@ FineTuningJobCancelledWebhookEvent, FineTuningJobFailedWebhookEvent, FineTuningJobSucceededWebhookEvent, + RealtimeCallIncomingWebhookEvent, ResponseCancelledWebhookEvent, ResponseCompletedWebhookEvent, ResponseFailedWebhookEvent, diff --git a/tests/api_resources/beta/realtime/test_sessions.py b/tests/api_resources/beta/realtime/test_sessions.py deleted file mode 100644 index 3c55abf80c..0000000000 --- a/tests/api_resources/beta/realtime/test_sessions.py +++ /dev/null @@ -1,166 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from openai import OpenAI, AsyncOpenAI -from tests.utils import assert_matches_type -from openai.types.beta.realtime import SessionCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestSessions: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: OpenAI) -> None: - session = client.beta.realtime.sessions.create() - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: OpenAI) -> None: - session = client.beta.realtime.sessions.create( - client_secret={ - "expires_after": { - "anchor": "created_at", - "seconds": 0, - } - }, - input_audio_format="pcm16", - input_audio_noise_reduction={"type": "near_field"}, - input_audio_transcription={ - "language": "language", - "model": "model", - "prompt": "prompt", - }, - instructions="instructions", - max_response_output_tokens=0, - modalities=["text"], - model="gpt-4o-realtime-preview", - output_audio_format="pcm16", - speed=0.25, - temperature=0, - tool_choice="tool_choice", - tools=[ - { - "description": "description", - "name": "name", - "parameters": {}, - "type": "function", - } - ], - tracing="auto", - turn_detection={ - "create_response": True, - "eagerness": "low", - "interrupt_response": True, - "prefix_padding_ms": 0, - "silence_duration_ms": 0, - "threshold": 0, - "type": "server_vad", - }, - voice="ash", - ) - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.realtime.sessions.with_raw_response.create() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - session = response.parse() - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.realtime.sessions.with_streaming_response.create() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - session = response.parse() - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncSessions: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncOpenAI) -> None: - session = await async_client.beta.realtime.sessions.create() - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: - session = await async_client.beta.realtime.sessions.create( - client_secret={ - "expires_after": { - "anchor": "created_at", - "seconds": 0, - } - }, - input_audio_format="pcm16", - input_audio_noise_reduction={"type": "near_field"}, - input_audio_transcription={ - "language": "language", - "model": "model", - "prompt": "prompt", - }, - instructions="instructions", - max_response_output_tokens=0, - modalities=["text"], - model="gpt-4o-realtime-preview", - output_audio_format="pcm16", - speed=0.25, - temperature=0, - tool_choice="tool_choice", - tools=[ - { - "description": "description", - "name": "name", - "parameters": {}, - "type": "function", - } - ], - tracing="auto", - turn_detection={ - "create_response": True, - "eagerness": "low", - "interrupt_response": True, - "prefix_padding_ms": 0, - "silence_duration_ms": 0, - "threshold": 0, - "type": "server_vad", - }, - voice="ash", - ) - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.realtime.sessions.with_raw_response.create() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - session = response.parse() - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.realtime.sessions.with_streaming_response.create() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - session = await response.parse() - assert_matches_type(SessionCreateResponse, session, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/realtime/test_transcription_sessions.py b/tests/api_resources/beta/realtime/test_transcription_sessions.py deleted file mode 100644 index ac52489e74..0000000000 --- a/tests/api_resources/beta/realtime/test_transcription_sessions.py +++ /dev/null @@ -1,134 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from openai import OpenAI, AsyncOpenAI -from tests.utils import assert_matches_type -from openai.types.beta.realtime import TranscriptionSession - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestTranscriptionSessions: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: OpenAI) -> None: - transcription_session = client.beta.realtime.transcription_sessions.create() - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: OpenAI) -> None: - transcription_session = client.beta.realtime.transcription_sessions.create( - client_secret={ - "expires_at": { - "anchor": "created_at", - "seconds": 0, - } - }, - include=["string"], - input_audio_format="pcm16", - input_audio_noise_reduction={"type": "near_field"}, - input_audio_transcription={ - "language": "language", - "model": "gpt-4o-transcribe", - "prompt": "prompt", - }, - modalities=["text"], - turn_detection={ - "create_response": True, - "eagerness": "low", - "interrupt_response": True, - "prefix_padding_ms": 0, - "silence_duration_ms": 0, - "threshold": 0, - "type": "server_vad", - }, - ) - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.realtime.transcription_sessions.with_raw_response.create() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - transcription_session = response.parse() - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.realtime.transcription_sessions.with_streaming_response.create() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - transcription_session = response.parse() - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncTranscriptionSessions: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncOpenAI) -> None: - transcription_session = await async_client.beta.realtime.transcription_sessions.create() - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: - transcription_session = await async_client.beta.realtime.transcription_sessions.create( - client_secret={ - "expires_at": { - "anchor": "created_at", - "seconds": 0, - } - }, - include=["string"], - input_audio_format="pcm16", - input_audio_noise_reduction={"type": "near_field"}, - input_audio_transcription={ - "language": "language", - "model": "gpt-4o-transcribe", - "prompt": "prompt", - }, - modalities=["text"], - turn_detection={ - "create_response": True, - "eagerness": "low", - "interrupt_response": True, - "prefix_padding_ms": 0, - "silence_duration_ms": 0, - "threshold": 0, - "type": "server_vad", - }, - ) - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.realtime.transcription_sessions.with_raw_response.create() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - transcription_session = response.parse() - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.realtime.transcription_sessions.with_streaming_response.create() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - transcription_session = await response.parse() - assert_matches_type(TranscriptionSession, transcription_session, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/test_realtime.py b/tests/api_resources/beta/test_realtime.py index 2b0c7f7d8d..8f752a0fd3 100644 --- a/tests/api_resources/beta/test_realtime.py +++ b/tests/api_resources/beta/test_realtime.py @@ -6,6 +6,8 @@ import pytest +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") diff --git a/tests/api_resources/beta/realtime/__init__.py b/tests/api_resources/realtime/__init__.py similarity index 100% rename from tests/api_resources/beta/realtime/__init__.py rename to tests/api_resources/realtime/__init__.py diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py new file mode 100644 index 0000000000..c477268ee6 --- /dev/null +++ b/tests/api_resources/realtime/test_client_secrets.py @@ -0,0 +1,208 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.realtime import ClientSecretCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestClientSecrets: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + client_secret = client.realtime.client_secrets.create() + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + client_secret = client.realtime.client_secrets.create( + expires_after={ + "anchor": "created_at", + "seconds": 10, + }, + session={ + "model": "string", + "type": "realtime", + "audio": { + "input": { + "format": "pcm16", + "noise_reduction": {"type": "near_field"}, + "transcription": { + "language": "language", + "model": "whisper-1", + "prompt": "prompt", + }, + "turn_detection": { + "create_response": True, + "eagerness": "low", + "idle_timeout_ms": 0, + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + "type": "server_vad", + }, + }, + "output": { + "format": "pcm16", + "speed": 0.25, + "voice": "ash", + }, + }, + "client_secret": { + "expires_after": { + "anchor": "created_at", + "seconds": 0, + } + }, + "include": ["item.input_audio_transcription.logprobs"], + "instructions": "instructions", + "max_output_tokens": 0, + "output_modalities": ["text"], + "prompt": { + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + "temperature": 0, + "tool_choice": "none", + "tools": [ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + "tracing": "auto", + "truncation": "auto", + }, + ) + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.realtime.client_secrets.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client_secret = response.parse() + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.realtime.client_secrets.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client_secret = response.parse() + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncClientSecrets: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + client_secret = await async_client.realtime.client_secrets.create() + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + client_secret = await async_client.realtime.client_secrets.create( + expires_after={ + "anchor": "created_at", + "seconds": 10, + }, + session={ + "model": "string", + "type": "realtime", + "audio": { + "input": { + "format": "pcm16", + "noise_reduction": {"type": "near_field"}, + "transcription": { + "language": "language", + "model": "whisper-1", + "prompt": "prompt", + }, + "turn_detection": { + "create_response": True, + "eagerness": "low", + "idle_timeout_ms": 0, + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + "type": "server_vad", + }, + }, + "output": { + "format": "pcm16", + "speed": 0.25, + "voice": "ash", + }, + }, + "client_secret": { + "expires_after": { + "anchor": "created_at", + "seconds": 0, + } + }, + "include": ["item.input_audio_transcription.logprobs"], + "instructions": "instructions", + "max_output_tokens": 0, + "output_modalities": ["text"], + "prompt": { + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + "temperature": 0, + "tool_choice": "none", + "tools": [ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + "tracing": "auto", + "truncation": "auto", + }, + ) + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.realtime.client_secrets.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + client_secret = response.parse() + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.realtime.client_secrets.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + client_secret = await response.parse() + assert_matches_type(ClientSecretCreateResponse, client_secret, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_realtime.py b/tests/api_resources/test_realtime.py new file mode 100644 index 0000000000..2b0c7f7d8d --- /dev/null +++ b/tests/api_resources/test_realtime.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os + +import pytest + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRealtime: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + +class TestAsyncRealtime: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) From c382ea30742f9327ad2afc25c8febb148452694a Mon Sep 17 00:00:00 2001 From: David Meadows Date: Tue, 2 Sep 2025 09:49:33 -0400 Subject: [PATCH 085/408] chore(client): format imports --- tests/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils.py b/tests/utils.py index 7740ed3f7c..a07052140b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -5,7 +5,7 @@ import inspect import traceback import contextlib -from typing import Any, TypeVar, Iterator, ForwardRef, Sequence, cast +from typing import Any, TypeVar, Iterator, Sequence, ForwardRef, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type From 3e3c7a762098d30ebd30567a9853bd5917354987 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 13:50:11 +0000 Subject: [PATCH 086/408] release: 1.103.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 98411f0f2b..0a5613fed8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.102.0" + ".": "1.103.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 26ca1c5cb2..6595e5246b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 1.103.0 (2025-09-02) + +Full Changelog: [v1.102.0...v1.103.0](https://github.com/openai/openai-python/compare/v1.102.0...v1.103.0) + +### Features + +* **api:** realtime API updates ([b7c2ddc](https://github.com/openai/openai-python/commit/b7c2ddc5e5dedda01015b3d0e14ea6eb68c282d3)) + + +### Bug Fixes + +* **responses:** add missing params to stream() method ([bfc0673](https://github.com/openai/openai-python/commit/bfc06732ffe3764cb95cef9f23b4b5c0d312826a)) + + +### Chores + +* bump `inline-snapshot` version to 0.28.0 ([#2590](https://github.com/openai/openai-python/issues/2590)) ([a6b0872](https://github.com/openai/openai-python/commit/a6b087226587d4cc4f59f1f09a595921b2823ef2)) +* **client:** format imports ([7ae3020](https://github.com/openai/openai-python/commit/7ae3020b3ca7de21e6e9a0a1c40908e655f6cad5)) +* **internal:** add Sequence related utils ([d3d72b9](https://github.com/openai/openai-python/commit/d3d72b9ce3c0885bf2b6934ac57d9e84f8653208)) +* **internal:** fix formatting ([3ab273f](https://github.com/openai/openai-python/commit/3ab273f21e601f088be7502b7bb5d249fc386d6a)) +* **internal:** minor formatting change ([478a348](https://github.com/openai/openai-python/commit/478a34881c968e9cab9d93ac2cf8da2fcb37c46c)) +* **internal:** update pyright exclude list ([66e440f](https://github.com/openai/openai-python/commit/66e440fac3ca388400392c64211450dedc491c11)) + ## 1.102.0 (2025-08-26) Full Changelog: [v1.101.0...v1.102.0](https://github.com/openai/openai-python/compare/v1.101.0...v1.102.0) diff --git a/pyproject.toml b/pyproject.toml index 2633918fc0..309b0f5544 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.102.0" +version = "1.103.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b2d62263ff..313e60b0bf 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.102.0" # x-release-please-version +__version__ = "1.103.0" # x-release-please-version From 7da727a4a3eb35306c328e2c3207a1618ed1809f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:11:31 +0000 Subject: [PATCH 087/408] feat(types): replace List[str] with SequenceNotStr in params --- src/openai/_utils/_transform.py | 6 ++++ .../resources/chat/completions/completions.py | 18 +++++----- src/openai/resources/completions.py | 36 +++++++++---------- src/openai/resources/containers/containers.py | 7 ++-- src/openai/resources/embeddings.py | 8 ++--- .../fine_tuning/checkpoints/permissions.py | 7 ++-- src/openai/resources/images.py | 20 +++++------ src/openai/resources/moderations.py | 8 ++--- src/openai/resources/uploads/uploads.py | 7 ++-- .../resources/vector_stores/file_batches.py | 8 ++--- .../resources/vector_stores/vector_stores.py | 12 +++---- .../types/beta/assistant_create_params.py | 9 ++--- .../types/beta/assistant_update_params.py | 7 ++-- .../beta/thread_create_and_run_params.py | 13 +++---- src/openai/types/beta/thread_create_params.py | 9 ++--- src/openai/types/beta/thread_update_params.py | 7 ++-- .../types/chat/completion_create_params.py | 3 +- src/openai/types/completion_create_params.py | 7 ++-- src/openai/types/container_create_params.py | 5 +-- src/openai/types/embedding_create_params.py | 5 +-- src/openai/types/eval_create_params.py | 7 ++-- src/openai/types/evals/run_create_params.py | 7 ++-- .../checkpoints/permission_create_params.py | 5 +-- .../types/fine_tuning/job_create_params.py | 5 +-- .../types/graders/label_model_grader_param.py | 7 ++-- src/openai/types/image_edit_params.py | 6 ++-- src/openai/types/moderation_create_params.py | 5 +-- .../realtime/realtime_tools_config_param.py | 10 +++--- .../realtime_tools_config_union_param.py | 12 ++++--- .../types/responses/file_search_tool_param.py | 5 +-- .../response_computer_tool_call_param.py | 6 ++-- .../response_file_search_tool_call_param.py | 6 ++-- .../responses/response_input_item_param.py | 5 +-- .../types/responses/response_input_param.py | 3 +- src/openai/types/responses/tool_param.py | 13 +++---- .../types/responses/web_search_tool_param.py | 6 ++-- src/openai/types/upload_complete_params.py | 5 +-- .../types/vector_store_create_params.py | 5 +-- .../types/vector_store_search_params.py | 5 +-- .../vector_stores/file_batch_create_params.py | 5 +-- 40 files changed, 183 insertions(+), 147 deletions(-) diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 4fd49a1908..f5c41c09c4 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -16,6 +16,7 @@ lru_cache, is_mapping, is_iterable, + is_sequence, ) from .._files import is_base64_file_input from ._typing import ( @@ -24,6 +25,7 @@ extract_type_arg, is_iterable_type, is_required_type, + is_sequence_type, is_annotated_type, strip_annotated_type, ) @@ -184,6 +186,8 @@ def _transform_recursive( (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. @@ -346,6 +350,8 @@ async def _async_transform_recursive( (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 7e209ff0ee..14a755a50e 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -19,7 +19,7 @@ MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr from ...._utils import required_args, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -260,7 +260,7 @@ def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -549,7 +549,7 @@ def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -837,7 +837,7 @@ def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1124,7 +1124,7 @@ def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -1696,7 +1696,7 @@ async def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, @@ -1985,7 +1985,7 @@ async def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2273,7 +2273,7 @@ async def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2560,7 +2560,7 @@ async def create( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 43b923b9b9..97a84575ab 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -2,14 +2,14 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, overload import httpx from .. import _legacy_response from ..types import completion_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr from .._utils import required_args, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -49,7 +49,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -59,7 +59,7 @@ def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -204,7 +204,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: Literal[True], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -215,7 +215,7 @@ def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -359,7 +359,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: bool, best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -370,7 +370,7 @@ def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -514,7 +514,7 @@ def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -524,7 +524,7 @@ def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -599,7 +599,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -609,7 +609,7 @@ async def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, @@ -754,7 +754,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: Literal[True], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -765,7 +765,7 @@ async def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -909,7 +909,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: bool, best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, @@ -920,7 +920,7 @@ async def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1064,7 +1064,7 @@ async def create( self, *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], - prompt: Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None], + prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], best_of: Optional[int] | NotGiven = NOT_GIVEN, echo: Optional[bool] | NotGiven = NOT_GIVEN, frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, @@ -1074,7 +1074,7 @@ async def create( n: Optional[int] | NotGiven = NOT_GIVEN, presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, suffix: Optional[str] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py index 71e5e6b08d..30e9eff127 100644 --- a/src/openai/resources/containers/containers.py +++ b/src/openai/resources/containers/containers.py @@ -2,14 +2,13 @@ from __future__ import annotations -from typing import List from typing_extensions import Literal import httpx from ... import _legacy_response from ...types import container_list_params, container_create_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, SequenceNotStr from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -60,7 +59,7 @@ def create( *, name: str, expires_after: container_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, + file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -256,7 +255,7 @@ async def create( *, name: str, expires_after: container_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, + file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 609f33f3b4..a8cf179850 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -4,14 +4,14 @@ import array import base64 -from typing import List, Union, Iterable, cast +from typing import Union, Iterable, cast from typing_extensions import Literal import httpx from .. import _legacy_response from ..types import embedding_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr from .._utils import is_given, maybe_transform from .._compat import cached_property from .._extras import numpy as np, has_numpy @@ -47,7 +47,7 @@ def with_streaming_response(self) -> EmbeddingsWithStreamingResponse: def create( self, *, - input: Union[str, List[str], Iterable[int], Iterable[Iterable[int]]], + input: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]], model: Union[str, EmbeddingModel], dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, @@ -166,7 +166,7 @@ def with_streaming_response(self) -> AsyncEmbeddingsWithStreamingResponse: async def create( self, *, - input: Union[str, List[str], Iterable[int], Iterable[Iterable[int]]], + input: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]], model: Union[str, EmbeddingModel], dimensions: int | NotGiven = NOT_GIVEN, encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py index 547e42ecac..f8ae125941 100644 --- a/src/openai/resources/fine_tuning/checkpoints/permissions.py +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -2,13 +2,12 @@ from __future__ import annotations -from typing import List from typing_extensions import Literal import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -47,7 +46,7 @@ def create( self, fine_tuned_model_checkpoint: str, *, - project_ids: List[str], + project_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -215,7 +214,7 @@ def create( self, fine_tuned_model_checkpoint: str, *, - project_ids: List[str], + project_ids: SequenceNotStr[str], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index c8eda8a76f..17ec264b6a 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -2,14 +2,14 @@ from __future__ import annotations -from typing import List, Union, Mapping, Optional, cast +from typing import Union, Mapping, Optional, cast from typing_extensions import Literal, overload import httpx from .. import _legacy_response from ..types import image_edit_params, image_generate_params, image_create_variation_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr from .._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -121,7 +121,7 @@ def create_variation( def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, @@ -234,7 +234,7 @@ def edit( def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: Literal[True], background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, @@ -347,7 +347,7 @@ def edit( def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: bool, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, @@ -460,7 +460,7 @@ def edit( def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, @@ -1009,7 +1009,7 @@ async def create_variation( async def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, @@ -1122,7 +1122,7 @@ async def edit( async def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: Literal[True], background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, @@ -1235,7 +1235,7 @@ async def edit( async def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: bool, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, @@ -1348,7 +1348,7 @@ async def edit( async def edit( self, *, - image: Union[FileTypes, List[FileTypes]], + image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index f7a8b52c23..91c0df4358 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -2,13 +2,13 @@ from __future__ import annotations -from typing import List, Union, Iterable +from typing import Union, Iterable import httpx from .. import _legacy_response from ..types import moderation_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -44,7 +44,7 @@ def with_streaming_response(self) -> ModerationsWithStreamingResponse: def create( self, *, - input: Union[str, List[str], Iterable[ModerationMultiModalInputParam]], + input: Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]], model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -114,7 +114,7 @@ def with_streaming_response(self) -> AsyncModerationsWithStreamingResponse: async def create( self, *, - input: Union[str, List[str], Iterable[ModerationMultiModalInputParam]], + input: Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]], model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 125a45e33c..c1dd4ec7c7 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -8,7 +8,6 @@ import builtins from typing import List, overload from pathlib import Path - import anyio import httpx @@ -22,7 +21,7 @@ AsyncPartsWithStreamingResponse, ) from ...types import FilePurpose, upload_create_params, upload_complete_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -281,7 +280,7 @@ def complete( self, upload_id: str, *, - part_ids: List[str], + part_ids: SequenceNotStr[str], md5: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -589,7 +588,7 @@ async def complete( self, upload_id: str, *, - part_ids: List[str], + part_ids: SequenceNotStr[str], md5: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index 4dd4430b71..5c1470b3c3 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -3,7 +3,7 @@ from __future__ import annotations import asyncio -from typing import Dict, List, Iterable, Optional +from typing import Dict, Iterable, Optional from typing_extensions import Union, Literal from concurrent.futures import Future, ThreadPoolExecutor, as_completed @@ -12,7 +12,7 @@ from ... import _legacy_response from ...types import FileChunkingStrategyParam -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr from ..._utils import is_given, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -52,7 +52,7 @@ def create( self, vector_store_id: str, *, - file_ids: List[str], + file_ids: SequenceNotStr[str], attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -389,7 +389,7 @@ async def create( self, vector_store_id: str, *, - file_ids: List[str], + file_ids: SequenceNotStr[str], attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py index 9fc17b183b..4f211ea25a 100644 --- a/src/openai/resources/vector_stores/vector_stores.py +++ b/src/openai/resources/vector_stores/vector_stores.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import Union, Optional from typing_extensions import Literal import httpx @@ -23,7 +23,7 @@ vector_store_search_params, vector_store_update_params, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -80,7 +80,7 @@ def create( *, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, + file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -332,7 +332,7 @@ def search( self, vector_store_id: str, *, - query: Union[str, List[str]], + query: Union[str, SequenceNotStr[str]], filters: vector_store_search_params.Filters | NotGiven = NOT_GIVEN, max_num_results: int | NotGiven = NOT_GIVEN, ranking_options: vector_store_search_params.RankingOptions | NotGiven = NOT_GIVEN, @@ -425,7 +425,7 @@ async def create( *, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: List[str] | NotGiven = NOT_GIVEN, + file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, name: str | NotGiven = NOT_GIVEN, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -677,7 +677,7 @@ def search( self, vector_store_id: str, *, - query: Union[str, List[str]], + query: Union[str, SequenceNotStr[str]], filters: vector_store_search_params.Filters | NotGiven = NOT_GIVEN, max_num_results: int | NotGiven = NOT_GIVEN, ranking_options: vector_store_search_params.RankingOptions | NotGiven = NOT_GIVEN, diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 4b03dc0ea6..07f8f28f02 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared.chat_model import ChatModel from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata @@ -123,7 +124,7 @@ class AssistantCreateParams(TypedDict, total=False): class ToolResourcesCodeInterpreter(TypedDict, total=False): - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files @@ -170,7 +171,7 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): If not set, will use the `auto` strategy. """ - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to add to the vector store. There can be a maximum of 10000 files in a vector @@ -189,7 +190,7 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): class ToolResourcesFileSearch(TypedDict, total=False): - vector_store_ids: List[str] + vector_store_ids: SequenceNotStr[str] """ The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index e032554db8..45d9f984b2 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, TypedDict +from ..._types import SequenceNotStr from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort @@ -158,7 +159,7 @@ class AssistantUpdateParams(TypedDict, total=False): class ToolResourcesCodeInterpreter(TypedDict, total=False): - file_ids: List[str] + file_ids: SequenceNotStr[str] """ Overrides the list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available @@ -168,7 +169,7 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): class ToolResourcesFileSearch(TypedDict, total=False): - vector_store_ids: List[str] + vector_store_ids: SequenceNotStr[str] """ Overrides the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index ad148d693a..734e5e2a4e 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared.chat_model import ChatModel from .assistant_tool_param import AssistantToolParam from ..shared_params.metadata import Metadata @@ -217,7 +218,7 @@ class ThreadMessage(TypedDict, total=False): class ThreadToolResourcesCodeInterpreter(TypedDict, total=False): - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files @@ -265,7 +266,7 @@ class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): If not set, will use the `auto` strategy. """ - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to add to the vector store. There can be a maximum of 10000 files in a vector @@ -284,7 +285,7 @@ class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): class ThreadToolResourcesFileSearch(TypedDict, total=False): - vector_store_ids: List[str] + vector_store_ids: SequenceNotStr[str] """ The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) @@ -334,7 +335,7 @@ class Thread(TypedDict, total=False): class ToolResourcesCodeInterpreter(TypedDict, total=False): - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files @@ -343,7 +344,7 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): class ToolResourcesFileSearch(TypedDict, total=False): - vector_store_ids: List[str] + vector_store_ids: SequenceNotStr[str] """ The ID of the [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index ec1ccf19a6..8fd9f38df7 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared_params.metadata import Metadata from .code_interpreter_tool_param import CodeInterpreterToolParam from .threads.message_content_part_param import MessageContentPartParam @@ -96,7 +97,7 @@ class Message(TypedDict, total=False): class ToolResourcesCodeInterpreter(TypedDict, total=False): - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files @@ -143,7 +144,7 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): If not set, will use the `auto` strategy. """ - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to add to the vector store. There can be a maximum of 10000 files in a vector @@ -162,7 +163,7 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): class ToolResourcesFileSearch(TypedDict, total=False): - vector_store_ids: List[str] + vector_store_ids: SequenceNotStr[str] """ The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py index b47ea8f3b0..464ea8d7eb 100644 --- a/src/openai/types/beta/thread_update_params.py +++ b/src/openai/types/beta/thread_update_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Optional +from typing import Optional from typing_extensions import TypedDict +from ..._types import SequenceNotStr from ..shared_params.metadata import Metadata __all__ = ["ThreadUpdateParams", "ToolResources", "ToolResourcesCodeInterpreter", "ToolResourcesFileSearch"] @@ -31,7 +32,7 @@ class ThreadUpdateParams(TypedDict, total=False): class ToolResourcesCodeInterpreter(TypedDict, total=False): - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files @@ -40,7 +41,7 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): class ToolResourcesFileSearch(TypedDict, total=False): - vector_store_ids: List[str] + vector_store_ids: SequenceNotStr[str] """ The [vector store](https://platform.openai.com/docs/api-reference/vector-stores/object) diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index da37ee4c13..2ae81dfbc2 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -5,6 +5,7 @@ from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared.chat_model import ChatModel from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort @@ -243,7 +244,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): parameter. """ - stop: Union[Optional[str], List[str], None] + stop: Union[Optional[str], SequenceNotStr[str], None] """Not supported with latest reasoning models `o3` and `o4-mini`. Up to 4 sequences where the API will stop generating further tokens. The diff --git a/src/openai/types/completion_create_params.py b/src/openai/types/completion_create_params.py index 6ae20cff83..f9beb9afc7 100644 --- a/src/openai/types/completion_create_params.py +++ b/src/openai/types/completion_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from .._types import SequenceNotStr from .chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam __all__ = ["CompletionCreateParamsBase", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming"] @@ -21,7 +22,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): them. """ - prompt: Required[Union[str, List[str], Iterable[int], Iterable[Iterable[int]], None]] + prompt: Required[Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None]] """ The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. @@ -119,7 +120,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): response parameter to monitor changes in the backend. """ - stop: Union[Optional[str], List[str], None] + stop: Union[Optional[str], SequenceNotStr[str], None] """Not supported with latest reasoning models `o3` and `o4-mini`. Up to 4 sequences where the API will stop generating further tokens. The diff --git a/src/openai/types/container_create_params.py b/src/openai/types/container_create_params.py index bd27334933..01a48ac410 100644 --- a/src/openai/types/container_create_params.py +++ b/src/openai/types/container_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List from typing_extensions import Literal, Required, TypedDict +from .._types import SequenceNotStr + __all__ = ["ContainerCreateParams", "ExpiresAfter"] @@ -15,7 +16,7 @@ class ContainerCreateParams(TypedDict, total=False): expires_after: ExpiresAfter """Container expiration time in seconds relative to the 'anchor' time.""" - file_ids: List[str] + file_ids: SequenceNotStr[str] """IDs of files to copy to the container.""" diff --git a/src/openai/types/embedding_create_params.py b/src/openai/types/embedding_create_params.py index 94edce10a4..ab3e877964 100644 --- a/src/openai/types/embedding_create_params.py +++ b/src/openai/types/embedding_create_params.py @@ -2,16 +2,17 @@ from __future__ import annotations -from typing import List, Union, Iterable +from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict +from .._types import SequenceNotStr from .embedding_model import EmbeddingModel __all__ = ["EmbeddingCreateParams"] class EmbeddingCreateParams(TypedDict, total=False): - input: Required[Union[str, List[str], Iterable[int], Iterable[Iterable[int]]]] + input: Required[Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]]] """Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index 9674785701..016f705dd7 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from .._types import SequenceNotStr from .shared_params.metadata import Metadata from .graders.python_grader_param import PythonGraderParam from .graders.score_model_grader_param import ScoreModelGraderParam @@ -159,7 +160,7 @@ class TestingCriterionLabelModel(TypedDict, total=False): May include variable references to the `item` namespace, ie {{item.name}}. """ - labels: Required[List[str]] + labels: Required[SequenceNotStr[str]] """The labels to classify to each item in the evaluation.""" model: Required[str] @@ -168,7 +169,7 @@ class TestingCriterionLabelModel(TypedDict, total=False): name: Required[str] """The name of the grader.""" - passing_labels: Required[List[str]] + passing_labels: Required[SequenceNotStr[str]] """The labels that indicate a passing result. Must be a subset of labels.""" type: Required[Literal["label_model"]] diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index 1622b00eb7..faf06a2f58 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..responses.tool_param import ToolParam from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort @@ -119,13 +120,13 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total temperature: Optional[float] """Sampling temperature. This is a query parameter used to select responses.""" - tools: Optional[List[str]] + tools: Optional[SequenceNotStr[str]] """List of tool names. This is a query parameter used to select responses.""" top_p: Optional[float] """Nucleus sampling parameter. This is a query parameter used to select responses.""" - users: Optional[List[str]] + users: Optional[SequenceNotStr[str]] """List of user identifiers. This is a query parameter used to select responses.""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_create_params.py b/src/openai/types/fine_tuning/checkpoints/permission_create_params.py index 92f98f21b9..e7cf4e4ee4 100644 --- a/src/openai/types/fine_tuning/checkpoints/permission_create_params.py +++ b/src/openai/types/fine_tuning/checkpoints/permission_create_params.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing import List from typing_extensions import Required, TypedDict +from ...._types import SequenceNotStr + __all__ = ["PermissionCreateParams"] class PermissionCreateParams(TypedDict, total=False): - project_ids: Required[List[str]] + project_ids: Required[SequenceNotStr[str]] """The project identifiers to grant access to.""" diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 5514db1ed1..351d4e0e1b 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from ..._types import SequenceNotStr from .dpo_method_param import DpoMethodParam from ..shared_params.metadata import Metadata from .supervised_method_param import SupervisedMethodParam @@ -137,7 +138,7 @@ class IntegrationWandb(TypedDict, total=False): If not set, we will use the Job ID as the name. """ - tags: List[str] + tags: SequenceNotStr[str] """A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some default tags are generated diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py index 941c8a1bd0..57f7885872 100644 --- a/src/openai/types/graders/label_model_grader_param.py +++ b/src/openai/types/graders/label_model_grader_param.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Iterable +from typing import Union, Iterable from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..responses.response_input_text_param import ResponseInputTextParam __all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] @@ -54,7 +55,7 @@ class Input(TypedDict, total=False): class LabelModelGraderParam(TypedDict, total=False): input: Required[Iterable[Input]] - labels: Required[List[str]] + labels: Required[SequenceNotStr[str]] """The labels to assign to each item in the evaluation.""" model: Required[str] @@ -63,7 +64,7 @@ class LabelModelGraderParam(TypedDict, total=False): name: Required[str] """The name of the grader.""" - passing_labels: Required[List[str]] + passing_labels: Required[SequenceNotStr[str]] """The labels that indicate a passing result. Must be a subset of labels.""" type: Required[Literal["label_model"]] diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index c0481012e4..065d9789fc 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -2,17 +2,17 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import Union, Optional from typing_extensions import Literal, Required, TypedDict -from .._types import FileTypes +from .._types import FileTypes, SequenceNotStr from .image_model import ImageModel __all__ = ["ImageEditParamsBase", "ImageEditParamsNonStreaming", "ImageEditParamsStreaming"] class ImageEditParamsBase(TypedDict, total=False): - image: Required[Union[FileTypes, List[FileTypes]]] + image: Required[Union[FileTypes, SequenceNotStr[FileTypes]]] """The image(s) to edit. Must be a supported image file or an array of images. For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than diff --git a/src/openai/types/moderation_create_params.py b/src/openai/types/moderation_create_params.py index 3ea2f3cd88..65d9b7e561 100644 --- a/src/openai/types/moderation_create_params.py +++ b/src/openai/types/moderation_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Iterable +from typing import Union, Iterable from typing_extensions import Required, TypedDict +from .._types import SequenceNotStr from .moderation_model import ModerationModel from .moderation_multi_modal_input_param import ModerationMultiModalInputParam @@ -12,7 +13,7 @@ class ModerationCreateParams(TypedDict, total=False): - input: Required[Union[str, List[str], Iterable[ModerationMultiModalInputParam]]] + input: Required[Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]]] """Input (or inputs) to classify. Can be a single string, an array of strings, or an array of multi-modal input diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py index 12af65c871..ea4b8c4d43 100644 --- a/src/openai/types/realtime/realtime_tools_config_param.py +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -5,6 +5,8 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr + __all__ = [ "RealtimeToolsConfigParam", "RealtimeToolsConfigUnionParam", @@ -45,11 +47,11 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" -McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter] +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): @@ -61,7 +63,7 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" @@ -74,7 +76,7 @@ class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py index 1b9f18536c..21b4d07752 100644 --- a/src/openai/types/realtime/realtime_tools_config_union_param.py +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr + __all__ = [ "RealtimeToolsConfigUnionParam", "Function", @@ -44,11 +46,11 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" -McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter] +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): @@ -60,7 +62,7 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" @@ -73,7 +75,7 @@ class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" diff --git a/src/openai/types/responses/file_search_tool_param.py b/src/openai/types/responses/file_search_tool_param.py index 2851fae460..c7641c1b86 100644 --- a/src/openai/types/responses/file_search_tool_param.py +++ b/src/openai/types/responses/file_search_tool_param.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union, Optional +from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared_params.compound_filter import CompoundFilter from ..shared_params.comparison_filter import ComparisonFilter @@ -29,7 +30,7 @@ class FileSearchToolParam(TypedDict, total=False): type: Required[Literal["file_search"]] """The type of the file search tool. Always `file_search`.""" - vector_store_ids: Required[List[str]] + vector_store_ids: Required[SequenceNotStr[str]] """The IDs of the vector stores to search.""" filters: Optional[Filters] diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py index d4ef56ab5c..0be63db2fe 100644 --- a/src/openai/types/responses/response_computer_tool_call_param.py +++ b/src/openai/types/responses/response_computer_tool_call_param.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import List, Union, Iterable +from typing import Union, Iterable from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr + __all__ = [ "ResponseComputerToolCallParam", "Action", @@ -86,7 +88,7 @@ class ActionDrag(TypedDict, total=False): class ActionKeypress(TypedDict, total=False): - keys: Required[List[str]] + keys: Required[SequenceNotStr[str]] """The combination of keys the model is requesting to be pressed. This is an array of strings, each representing a key. diff --git a/src/openai/types/responses/response_file_search_tool_call_param.py b/src/openai/types/responses/response_file_search_tool_call_param.py index 9a4177cf81..4903dca4fb 100644 --- a/src/openai/types/responses/response_file_search_tool_call_param.py +++ b/src/openai/types/responses/response_file_search_tool_call_param.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from ..._types import SequenceNotStr + __all__ = ["ResponseFileSearchToolCallParam", "Result"] @@ -35,7 +37,7 @@ class ResponseFileSearchToolCallParam(TypedDict, total=False): id: Required[str] """The unique ID of the file search tool call.""" - queries: Required[List[str]] + queries: Required[SequenceNotStr[str]] """The queries used to search for files.""" status: Required[Literal["in_progress", "searching", "completed", "incomplete", "failed"]] diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 0d5dbda85c..5ad83fc03a 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Union, Iterable, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from .easy_input_message_param import EasyInputMessageParam from .response_output_message_param import ResponseOutputMessageParam from .response_reasoning_item_param import ResponseReasoningItemParam @@ -135,7 +136,7 @@ class ImageGenerationCall(TypedDict, total=False): class LocalShellCallAction(TypedDict, total=False): - command: Required[List[str]] + command: Required[SequenceNotStr[str]] """The command to run.""" env: Required[Dict[str, str]] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 6ff36a4238..73eac62428 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -5,6 +5,7 @@ from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from .easy_input_message_param import EasyInputMessageParam from .response_output_message_param import ResponseOutputMessageParam from .response_reasoning_item_param import ResponseReasoningItemParam @@ -136,7 +137,7 @@ class ImageGenerationCall(TypedDict, total=False): class LocalShellCallAction(TypedDict, total=False): - command: Required[List[str]] + command: Required[SequenceNotStr[str]] """The command to run.""" env: Required[Dict[str, str]] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index def1f08094..fd916a2a81 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -2,10 +2,11 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..chat import ChatCompletionFunctionToolParam +from ..._types import SequenceNotStr from .custom_tool_param import CustomToolParam from .computer_tool_param import ComputerToolParam from .function_tool_param import FunctionToolParam @@ -40,11 +41,11 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" -McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter] +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): @@ -56,7 +57,7 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" @@ -69,7 +70,7 @@ class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): it will match this filter. """ - tool_names: List[str] + tool_names: SequenceNotStr[str] """List of allowed tool names.""" @@ -152,7 +153,7 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): type: Required[Literal["auto"]] """Always `auto`.""" - file_ids: List[str] + file_ids: SequenceNotStr[str] """An optional list of uploaded files to make available to your code.""" diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py index 17a382456b..7fa19e9c23 100644 --- a/src/openai/types/responses/web_search_tool_param.py +++ b/src/openai/types/responses/web_search_tool_param.py @@ -2,14 +2,16 @@ from __future__ import annotations -from typing import List, Optional +from typing import Optional from typing_extensions import Literal, Required, TypedDict +from ..._types import SequenceNotStr + __all__ = ["WebSearchToolParam", "Filters", "UserLocation"] class Filters(TypedDict, total=False): - allowed_domains: Optional[List[str]] + allowed_domains: Optional[SequenceNotStr[str]] """Allowed domains for the search. If not provided, all domains are allowed. Subdomains of the provided domains are diff --git a/src/openai/types/upload_complete_params.py b/src/openai/types/upload_complete_params.py index cce568d5c6..846a241dc7 100644 --- a/src/openai/types/upload_complete_params.py +++ b/src/openai/types/upload_complete_params.py @@ -2,14 +2,15 @@ from __future__ import annotations -from typing import List from typing_extensions import Required, TypedDict +from .._types import SequenceNotStr + __all__ = ["UploadCompleteParams"] class UploadCompleteParams(TypedDict, total=False): - part_ids: Required[List[str]] + part_ids: Required[SequenceNotStr[str]] """The ordered list of Part IDs.""" md5: str diff --git a/src/openai/types/vector_store_create_params.py b/src/openai/types/vector_store_create_params.py index 365d0936b1..945a9886a3 100644 --- a/src/openai/types/vector_store_create_params.py +++ b/src/openai/types/vector_store_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Optional +from typing import Optional from typing_extensions import Literal, Required, TypedDict +from .._types import SequenceNotStr from .shared_params.metadata import Metadata from .file_chunking_strategy_param import FileChunkingStrategyParam @@ -22,7 +23,7 @@ class VectorStoreCreateParams(TypedDict, total=False): expires_after: ExpiresAfter """The expiration policy for a vector store.""" - file_ids: List[str] + file_ids: SequenceNotStr[str] """ A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access diff --git a/src/openai/types/vector_store_search_params.py b/src/openai/types/vector_store_search_params.py index 973c49ff5a..8b7b13c4a1 100644 --- a/src/openai/types/vector_store_search_params.py +++ b/src/openai/types/vector_store_search_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List, Union +from typing import Union from typing_extensions import Literal, Required, TypeAlias, TypedDict +from .._types import SequenceNotStr from .shared_params.compound_filter import CompoundFilter from .shared_params.comparison_filter import ComparisonFilter @@ -12,7 +13,7 @@ class VectorStoreSearchParams(TypedDict, total=False): - query: Required[Union[str, List[str]]] + query: Required[Union[str, SequenceNotStr[str]]] """A query string for a search""" filters: Filters diff --git a/src/openai/types/vector_stores/file_batch_create_params.py b/src/openai/types/vector_stores/file_batch_create_params.py index 1a470f757a..d8d7b44888 100644 --- a/src/openai/types/vector_stores/file_batch_create_params.py +++ b/src/openai/types/vector_stores/file_batch_create_params.py @@ -2,16 +2,17 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, Union, Optional from typing_extensions import Required, TypedDict +from ..._types import SequenceNotStr from ..file_chunking_strategy_param import FileChunkingStrategyParam __all__ = ["FileBatchCreateParams"] class FileBatchCreateParams(TypedDict, total=False): - file_ids: Required[List[str]] + file_ids: Required[SequenceNotStr[str]] """ A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access From 56baf2d26179b670b560dedacbffcaf828e53a13 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 2 Sep 2025 16:20:22 +0100 Subject: [PATCH 088/408] chore: remove unused import --- src/openai/resources/uploads/uploads.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index c1dd4ec7c7..8811bed48c 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -6,8 +6,9 @@ import os import logging import builtins -from typing import List, overload +from typing import overload from pathlib import Path + import anyio import httpx From 74d43eda0c295fbb931b589c02e078f50d92a82d Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 2 Sep 2025 17:46:20 +0100 Subject: [PATCH 089/408] fix(types): update some types to SequenceNotStr --- src/openai/resources/vector_stores/file_batches.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index 5c1470b3c3..adf399d8de 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -186,7 +186,7 @@ def create_and_poll( self, vector_store_id: str, *, - file_ids: List[str], + file_ids: SequenceNotStr[str], poll_interval_ms: int | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: @@ -320,7 +320,7 @@ def upload_and_poll( *, files: Iterable[FileTypes], max_concurrency: int = 5, - file_ids: List[str] = [], + file_ids: SequenceNotStr[str] = [], poll_interval_ms: int | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: @@ -523,7 +523,7 @@ async def create_and_poll( self, vector_store_id: str, *, - file_ids: List[str], + file_ids: SequenceNotStr[str], poll_interval_ms: int | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: @@ -657,7 +657,7 @@ async def upload_and_poll( *, files: Iterable[FileTypes], max_concurrency: int = 5, - file_ids: List[str] = [], + file_ids: SequenceNotStr[str] = [], poll_interval_ms: int | NotGiven = NOT_GIVEN, chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, ) -> VectorStoreFileBatch: From afe0aebc00323704eb0066e2f2df16d7ba9926f7 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 2 Sep 2025 17:48:04 +0100 Subject: [PATCH 090/408] fix(types): update more types to use SequenceNotStr --- src/openai/resources/chat/completions/completions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 14a755a50e..168cf04dbc 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -106,7 +106,7 @@ def parse( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1400,7 +1400,7 @@ def stream( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -1542,7 +1542,7 @@ async def parse( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, @@ -2836,7 +2836,7 @@ def stream( safety_identifier: str | NotGiven = NOT_GIVEN, seed: Optional[int] | NotGiven = NOT_GIVEN, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN, + stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, store: Optional[bool] | NotGiven = NOT_GIVEN, stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, temperature: Optional[float] | NotGiven = NOT_GIVEN, From f4458bc734db33f33a432fa62d8ef490e68d76db Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:49:05 +0000 Subject: [PATCH 091/408] release: 1.104.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0a5613fed8..2e568a21c7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.103.0" + ".": "1.104.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6595e5246b..6116a79d30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 1.104.0 (2025-09-02) + +Full Changelog: [v1.103.0...v1.104.0](https://github.com/openai/openai-python/compare/v1.103.0...v1.104.0) + +### Features + +* **types:** replace List[str] with SequenceNotStr in params ([bc00bda](https://github.com/openai/openai-python/commit/bc00bda880a80089be8a1758c016266ca72dab2c)) + + +### Bug Fixes + +* **types:** update more types to use SequenceNotStr ([cff135c](https://github.com/openai/openai-python/commit/cff135cb7059ef1bf8f9b101a83529fc0cee37c4)) +* **types:** update some types to SequenceNotStr ([03f8b88](https://github.com/openai/openai-python/commit/03f8b88a0d428b74a7822e678a60d0ef106ea961)) + + +### Chores + +* remove unused import ([ac7795b](https://github.com/openai/openai-python/commit/ac7795b50d956ec5dc468302e8e3579a0467edcb)) + ## 1.103.0 (2025-09-02) Full Changelog: [v1.102.0...v1.103.0](https://github.com/openai/openai-python/compare/v1.102.0...v1.103.0) diff --git a/pyproject.toml b/pyproject.toml index 309b0f5544..08a04d08d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.103.0" +version = "1.104.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 313e60b0bf..46e82bb627 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.103.0" # x-release-please-version +__version__ = "1.104.0" # x-release-please-version From 3c1f55439e0c6afe358a5e2d4ccfca9516b667af Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:55:13 +0000 Subject: [PATCH 092/408] chore(api): manual updates for ResponseInputAudio --- .stats.yml | 4 ++-- src/openai/resources/responses/responses.py | 12 +++++----- src/openai/types/eval_create_params.py | 2 ++ ...create_eval_completions_run_data_source.py | 2 ++ ..._eval_completions_run_data_source_param.py | 2 ++ src/openai/types/evals/run_cancel_response.py | 2 ++ src/openai/types/evals/run_create_params.py | 2 ++ src/openai/types/evals/run_create_response.py | 2 ++ src/openai/types/evals/run_list_response.py | 2 ++ .../types/evals/run_retrieve_response.py | 2 ++ .../types/graders/label_model_grader.py | 5 ++++- .../types/graders/label_model_grader_param.py | 8 ++++++- .../types/graders/score_model_grader.py | 5 ++++- .../types/graders/score_model_grader_param.py | 8 ++++++- src/openai/types/responses/__init__.py | 2 ++ src/openai/types/responses/response.py | 2 +- .../types/responses/response_create_params.py | 2 +- .../types/responses/response_input_audio.py | 22 +++++++++++++++++++ .../responses/response_input_audio_param.py | 22 +++++++++++++++++++ .../types/responses/response_input_content.py | 4 +++- .../responses/response_input_content_param.py | 5 ++++- ...sponse_input_message_content_list_param.py | 5 ++++- 22 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 src/openai/types/responses/response_input_audio.py create mode 100644 src/openai/types/responses/response_input_audio_param.py diff --git a/.stats.yml b/.stats.yml index ebe81d146e..41379b009a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-356b4364203ff36d7724074cd04f6e684253bfcc3c9d969122d730aa7bc51b46.yml -openapi_spec_hash: 4ab8e96f52699bc3d2b0c4432aa92af8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f312a661d9dd6b5d6d676e449c357f6414afd1fdaaf4d982d44ad86cba5c5f6e.yml +openapi_spec_hash: b62fd3d3fb98e37b1da0a2e22af51d40 config_hash: b854932c0ea24b400bdd64e4376936bd diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index e459f55c61..837d2b2211 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -269,7 +269,7 @@ def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. @@ -508,7 +508,7 @@ def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. @@ -747,7 +747,7 @@ def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. @@ -1700,7 +1700,7 @@ async def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. @@ -1939,7 +1939,7 @@ async def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. @@ -2178,7 +2178,7 @@ async def create( Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index 016f705dd7..eb7f86cd92 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -12,6 +12,7 @@ from .graders.string_check_grader_param import StringCheckGraderParam from .responses.response_input_text_param import ResponseInputTextParam from .graders.text_similarity_grader_param import TextSimilarityGraderParam +from .responses.response_input_audio_param import ResponseInputAudioParam __all__ = [ "EvalCreateParams", @@ -130,6 +131,7 @@ class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total= ResponseInputTextParam, TestingCriterionLabelModelInputEvalItemContentOutputText, TestingCriterionLabelModelInputEvalItemContentInputImage, + ResponseInputAudioParam, Iterable[object], ] diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index efcab9adb8..edf70c8ad4 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -9,6 +9,7 @@ from ..shared.response_format_text import ResponseFormatText from ..responses.easy_input_message import EasyInputMessage from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio from ..chat.chat_completion_function_tool import ChatCompletionFunctionTool from ..shared.response_format_json_object import ResponseFormatJSONObject from ..shared.response_format_json_schema import ResponseFormatJSONSchema @@ -114,6 +115,7 @@ class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): ResponseInputText, InputMessagesTemplateTemplateEvalItemContentOutputText, InputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, List[object], ] diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index effa658452..c14360ac80 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -9,6 +9,7 @@ from ..responses.easy_input_message_param import EasyInputMessageParam from ..shared_params.response_format_text import ResponseFormatText from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam from ..chat.chat_completion_function_tool_param import ChatCompletionFunctionToolParam from ..shared_params.response_format_json_object import ResponseFormatJSONObject from ..shared_params.response_format_json_schema import ResponseFormatJSONSchema @@ -112,6 +113,7 @@ class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=Fa ResponseInputTextParam, InputMessagesTemplateTemplateEvalItemContentOutputText, InputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudioParam, Iterable[object], ] diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 7f4f4c9cc4..44f9cfc453 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -12,6 +12,7 @@ from ..shared.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from ..responses.response_format_text_config import ResponseFormatTextConfig from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource @@ -158,6 +159,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, List[object], ] diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index faf06a2f58..ef9541ff0a 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -10,6 +10,7 @@ from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam from .create_eval_jsonl_run_data_source_param import CreateEvalJSONLRunDataSourceParam from ..responses.response_format_text_config_param import ResponseFormatTextConfigParam from .create_eval_completions_run_data_source_param import CreateEvalCompletionsRunDataSourceParam @@ -176,6 +177,7 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva ResponseInputTextParam, DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText, DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudioParam, Iterable[object], ] diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index fba5321552..70641d6db8 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -12,6 +12,7 @@ from ..shared.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from ..responses.response_format_text_config import ResponseFormatTextConfig from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource @@ -158,6 +159,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, List[object], ] diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index e9e445af5c..e31d570a84 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -12,6 +12,7 @@ from ..shared.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from ..responses.response_format_text_config import ResponseFormatTextConfig from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource @@ -158,6 +159,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, List[object], ] diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index e13f1abe42..62213d3edd 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -12,6 +12,7 @@ from ..shared.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio from .create_eval_jsonl_run_data_source import CreateEvalJSONLRunDataSource from ..responses.response_format_text_config import ResponseFormatTextConfig from .create_eval_completions_run_data_source import CreateEvalCompletionsRunDataSource @@ -158,6 +159,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + ResponseInputAudio, List[object], ] diff --git a/src/openai/types/graders/label_model_grader.py b/src/openai/types/graders/label_model_grader.py index 76dbfb854a..0929349c24 100644 --- a/src/openai/types/graders/label_model_grader.py +++ b/src/openai/types/graders/label_model_grader.py @@ -5,6 +5,7 @@ from ..._models import BaseModel from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio __all__ = ["LabelModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] @@ -31,7 +32,9 @@ class InputContentInputImage(BaseModel): """ -InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText, InputContentInputImage, List[object]] +InputContent: TypeAlias = Union[ + str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, List[object] +] class Input(BaseModel): diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py index 57f7885872..7bd6fdb4a7 100644 --- a/src/openai/types/graders/label_model_grader_param.py +++ b/src/openai/types/graders/label_model_grader_param.py @@ -7,6 +7,7 @@ from ..._types import SequenceNotStr from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam __all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] @@ -34,7 +35,12 @@ class InputContentInputImage(TypedDict, total=False): InputContent: TypeAlias = Union[ - str, ResponseInputTextParam, InputContentOutputText, InputContentInputImage, Iterable[object] + str, + ResponseInputTextParam, + InputContentOutputText, + InputContentInputImage, + ResponseInputAudioParam, + Iterable[object], ] diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index e6af0ebcf7..fc221b8e41 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -5,6 +5,7 @@ from ..._models import BaseModel from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio __all__ = ["ScoreModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] @@ -31,7 +32,9 @@ class InputContentInputImage(BaseModel): """ -InputContent: TypeAlias = Union[str, ResponseInputText, InputContentOutputText, InputContentInputImage, List[object]] +InputContent: TypeAlias = Union[ + str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, List[object] +] class Input(BaseModel): diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 47c9928076..15100bb74b 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam __all__ = ["ScoreModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] @@ -33,7 +34,12 @@ class InputContentInputImage(TypedDict, total=False): InputContent: TypeAlias = Union[ - str, ResponseInputTextParam, InputContentOutputText, InputContentInputImage, Iterable[object] + str, + ResponseInputTextParam, + InputContentOutputText, + InputContentInputImage, + ResponseInputAudioParam, + Iterable[object], ] diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 8047f3c4d1..d59f0a74b8 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -38,6 +38,7 @@ from .tool_choice_allowed import ToolChoiceAllowed as ToolChoiceAllowed from .tool_choice_options import ToolChoiceOptions as ToolChoiceOptions from .response_error_event import ResponseErrorEvent as ResponseErrorEvent +from .response_input_audio import ResponseInputAudio as ResponseInputAudio from .response_input_image import ResponseInputImage as ResponseInputImage from .response_input_param import ResponseInputParam as ResponseInputParam from .response_output_item import ResponseOutputItem as ResponseOutputItem @@ -75,6 +76,7 @@ from .tool_choice_allowed_param import ToolChoiceAllowedParam as ToolChoiceAllowedParam from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent +from .response_input_audio_param import ResponseInputAudioParam as ResponseInputAudioParam from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam from .response_output_text_param import ResponseOutputTextParam as ResponseOutputTextParam from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 9f6fd3e2d2..163648ef3e 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -125,7 +125,7 @@ class Response(BaseModel): Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index eac249414a..be687c0aff 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -225,7 +225,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): Learn more about [built-in tools](https://platform.openai.com/docs/guides/tools). - **MCP Tools**: Integrations with third-party systems via custom MCP servers or - predefined connectors such as Google Drive and Notion. Learn more about + predefined connectors such as Google Drive and SharePoint. Learn more about [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). - **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code with strongly typed arguments and outputs. diff --git a/src/openai/types/responses/response_input_audio.py b/src/openai/types/responses/response_input_audio.py new file mode 100644 index 0000000000..9fef6de0fd --- /dev/null +++ b/src/openai/types/responses/response_input_audio.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputAudio", "InputAudio"] + + +class InputAudio(BaseModel): + data: str + """Base64-encoded audio data.""" + + format: Literal["mp3", "wav"] + """The format of the audio data. Currently supported formats are `mp3` and `wav`.""" + + +class ResponseInputAudio(BaseModel): + input_audio: InputAudio + + type: Literal["input_audio"] + """The type of the input item. Always `input_audio`.""" diff --git a/src/openai/types/responses/response_input_audio_param.py b/src/openai/types/responses/response_input_audio_param.py new file mode 100644 index 0000000000..f3fc913cca --- /dev/null +++ b/src/openai/types/responses/response_input_audio_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputAudioParam", "InputAudio"] + + +class InputAudio(TypedDict, total=False): + data: Required[str] + """Base64-encoded audio data.""" + + format: Required[Literal["mp3", "wav"]] + """The format of the audio data. Currently supported formats are `mp3` and `wav`.""" + + +class ResponseInputAudioParam(TypedDict, total=False): + input_audio: Required[InputAudio] + + type: Required[Literal["input_audio"]] + """The type of the input item. Always `input_audio`.""" diff --git a/src/openai/types/responses/response_input_content.py b/src/openai/types/responses/response_input_content.py index 1726909a17..376b9ffce8 100644 --- a/src/openai/types/responses/response_input_content.py +++ b/src/openai/types/responses/response_input_content.py @@ -6,10 +6,12 @@ from ..._utils import PropertyInfo from .response_input_file import ResponseInputFile from .response_input_text import ResponseInputText +from .response_input_audio import ResponseInputAudio from .response_input_image import ResponseInputImage __all__ = ["ResponseInputContent"] ResponseInputContent: TypeAlias = Annotated[ - Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") + Union[ResponseInputText, ResponseInputImage, ResponseInputFile, ResponseInputAudio], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response_input_content_param.py b/src/openai/types/responses/response_input_content_param.py index 7791cdfd8e..a95e026a53 100644 --- a/src/openai/types/responses/response_input_content_param.py +++ b/src/openai/types/responses/response_input_content_param.py @@ -7,8 +7,11 @@ from .response_input_file_param import ResponseInputFileParam from .response_input_text_param import ResponseInputTextParam +from .response_input_audio_param import ResponseInputAudioParam from .response_input_image_param import ResponseInputImageParam __all__ = ["ResponseInputContentParam"] -ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] +ResponseInputContentParam: TypeAlias = Union[ + ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam, ResponseInputAudioParam +] diff --git a/src/openai/types/responses/response_input_message_content_list_param.py b/src/openai/types/responses/response_input_message_content_list_param.py index 080613df0d..8e3778d15a 100644 --- a/src/openai/types/responses/response_input_message_content_list_param.py +++ b/src/openai/types/responses/response_input_message_content_list_param.py @@ -7,10 +7,13 @@ from .response_input_file_param import ResponseInputFileParam from .response_input_text_param import ResponseInputTextParam +from .response_input_audio_param import ResponseInputAudioParam from .response_input_image_param import ResponseInputImageParam __all__ = ["ResponseInputMessageContentListParam", "ResponseInputContentParam"] -ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] +ResponseInputContentParam: TypeAlias = Union[ + ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam, ResponseInputAudioParam +] ResponseInputMessageContentListParam: TypeAlias = List[ResponseInputContentParam] From fb152d967edb181c1a17827f31a4df10e416e255 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:55:42 +0000 Subject: [PATCH 093/408] release: 1.104.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2e568a21c7..8168399b9e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.104.0" + ".": "1.104.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6116a79d30..422e50ed9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.104.1 (2025-09-02) + +Full Changelog: [v1.104.0...v1.104.1](https://github.com/openai/openai-python/compare/v1.104.0...v1.104.1) + +### Chores + +* **api:** manual updates for ResponseInputAudio ([0db5061](https://github.com/openai/openai-python/commit/0db50619663656ba97bba30ab640bbb33683d196)) + ## 1.104.0 (2025-09-02) Full Changelog: [v1.103.0...v1.104.0](https://github.com/openai/openai-python/compare/v1.103.0...v1.104.0) diff --git a/pyproject.toml b/pyproject.toml index 08a04d08d1..313eb21ea3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.104.0" +version = "1.104.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 46e82bb627..139d9a48ab 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.104.0" # x-release-please-version +__version__ = "1.104.1" # x-release-please-version From 5a6931dafdf73d9dbfce62c3a7c585b95daaf009 Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 2 Sep 2025 22:36:22 +0100 Subject: [PATCH 094/408] fix(types): add aliases back for web search tool types --- src/openai/types/responses/tool.py | 3 +++ src/openai/types/responses/tool_param.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 594e09d729..482d4e75c1 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -3,6 +3,7 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias +from . import web_search_tool from ..._utils import PropertyInfo from ..._models import BaseModel from .custom_tool import CustomTool @@ -30,6 +31,8 @@ "LocalShell", ] +WebSearchToolFilters = web_search_tool.Filters +WebSearchToolUserLocation = web_search_tool.UserLocation class McpAllowedToolsMcpToolFilter(BaseModel): read_only: Optional[bool] = None diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index fd916a2a81..54bc271c0f 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -5,6 +5,7 @@ from typing import Dict, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from . import web_search_tool_param from ..chat import ChatCompletionFunctionToolParam from ..._types import SequenceNotStr from .custom_tool_param import CustomToolParam @@ -31,6 +32,9 @@ "LocalShell", ] +WebSearchTool = web_search_tool_param.WebSearchToolParam +WebSearchToolFilters = web_search_tool_param.Filters +WebSearchToolUserLocation = web_search_tool_param.UserLocation class McpAllowedToolsMcpToolFilter(TypedDict, total=False): read_only: bool From a52463c93215a09f9a142e25c975935523d15c10 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 21:39:13 +0000 Subject: [PATCH 095/408] release: 1.104.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8168399b9e..a3896371d6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.104.1" + ".": "1.104.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 422e50ed9c..754f25576a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.104.2 (2025-09-02) + +Full Changelog: [v1.104.1...v1.104.2](https://github.com/openai/openai-python/compare/v1.104.1...v1.104.2) + +### Bug Fixes + +* **types:** add aliases back for web search tool types ([2521cd8](https://github.com/openai/openai-python/commit/2521cd8445906e418dbae783b0d7c375ad91d49d)) + ## 1.104.1 (2025-09-02) Full Changelog: [v1.104.0...v1.104.1](https://github.com/openai/openai-python/compare/v1.104.0...v1.104.1) diff --git a/pyproject.toml b/pyproject.toml index 313eb21ea3..6860630f3f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.104.1" +version = "1.104.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 139d9a48ab..4368a7e74c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.104.1" # x-release-please-version +__version__ = "1.104.2" # x-release-please-version From 2c60d78b378465433b70bbe2a7d3f94c8eeaa0d5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:10:24 +0000 Subject: [PATCH 096/408] feat(api): Add gpt-realtime models Adds gpt-realtime and gpt-realtime-2025-08-28 --- .stats.yml | 4 ++-- src/openai/types/realtime/realtime_session.py | 2 ++ src/openai/types/realtime/realtime_session_create_request.py | 2 ++ .../types/realtime/realtime_session_create_request_param.py | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 41379b009a..c41be6ee57 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f312a661d9dd6b5d6d676e449c357f6414afd1fdaaf4d982d44ad86cba5c5f6e.yml -openapi_spec_hash: b62fd3d3fb98e37b1da0a2e22af51d40 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-51afd6abbcb18c3086f62993f9379c18443b9e516cbc0548ddfb932e835657f8.yml +openapi_spec_hash: dae6afeaefa15cb8700c7a870531e06f config_hash: b854932c0ea24b400bdd64e4376936bd diff --git a/src/openai/types/realtime/realtime_session.py b/src/openai/types/realtime/realtime_session.py index 43576ea73d..fdb5e9419a 100644 --- a/src/openai/types/realtime/realtime_session.py +++ b/src/openai/types/realtime/realtime_session.py @@ -220,6 +220,8 @@ class RealtimeSession(BaseModel): model: Optional[ Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index a8d0f99704..85205add50 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -19,6 +19,8 @@ class RealtimeSessionCreateRequest(BaseModel): model: Union[ str, Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime", "gpt-4o-mini-realtime", "gpt-4o-realtime-preview", diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index 2c5d1e0bee..8f962ca0e2 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -21,6 +21,8 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): Union[ str, Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime", "gpt-4o-mini-realtime", "gpt-4o-realtime-preview", From 8672413735889e83e74e7e133b976fe6029843a5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:10:58 +0000 Subject: [PATCH 097/408] release: 1.105.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a3896371d6..1e15251d64 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.104.2" + ".": "1.105.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 754f25576a..3ed3bbe6ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.105.0 (2025-09-03) + +Full Changelog: [v1.104.2...v1.105.0](https://github.com/openai/openai-python/compare/v1.104.2...v1.105.0) + +### Features + +* **api:** Add gpt-realtime models ([8502041](https://github.com/openai/openai-python/commit/85020414808314df9cb42e020b11baff12f18f16)) + ## 1.104.2 (2025-09-02) Full Changelog: [v1.104.1...v1.104.2](https://github.com/openai/openai-python/compare/v1.104.1...v1.104.2) diff --git a/pyproject.toml b/pyproject.toml index 6860630f3f..587ca41e01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.104.2" +version = "1.105.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 4368a7e74c..5509cd4d8e 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.104.2" # x-release-please-version +__version__ = "1.105.0" # x-release-please-version From 25d16be18bcd11e00a853e8f4af881c76098e0d0 Mon Sep 17 00:00:00 2001 From: "Johan Stenberg (MSFT)" Date: Wed, 3 Sep 2025 08:52:05 -0700 Subject: [PATCH 098/408] feat(client): support callable api_key (#2588) Co-authored-by: Krista Pratico --- src/openai/_client.py | 47 ++++-- src/openai/lib/azure.py | 24 +-- .../resources/beta/realtime/realtime.py | 2 + src/openai/resources/realtime/realtime.py | 2 + tests/test_client.py | 138 +++++++++++++++++- 5 files changed, 188 insertions(+), 25 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index fe5ebac42a..2be32fe13f 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, Any, Union, Mapping +from typing import TYPE_CHECKING, Any, Union, Mapping, Callable, Awaitable from typing_extensions import Self, override import httpx @@ -25,6 +25,7 @@ get_async_library, ) from ._compat import cached_property +from ._models import FinalRequestOptions from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError @@ -96,7 +97,7 @@ class OpenAI(SyncAPIClient): def __init__( self, *, - api_key: str | None = None, + api_key: str | None | Callable[[], str] = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -134,7 +135,12 @@ def __init__( raise OpenAIError( "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" ) - self.api_key = api_key + if callable(api_key): + self.api_key = "" + self._api_key_provider: Callable[[], str] | None = api_key + else: + self.api_key = api_key + self._api_key_provider = None if organization is None: organization = os.environ.get("OPENAI_ORG_ID") @@ -295,6 +301,15 @@ def with_streaming_response(self) -> OpenAIWithStreamedResponse: def qs(self) -> Querystring: return Querystring(array_format="brackets") + def _refresh_api_key(self) -> None: + if self._api_key_provider: + self.api_key = self._api_key_provider() + + @override + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + self._refresh_api_key() + return super()._prepare_options(options) + @property @override def auth_headers(self) -> dict[str, str]: @@ -318,7 +333,7 @@ def default_headers(self) -> dict[str, str | Omit]: def copy( self, *, - api_key: str | None = None, + api_key: str | Callable[[], str] | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -356,7 +371,7 @@ def copy( http_client = http_client or self._client return self.__class__( - api_key=api_key or self.api_key, + api_key=api_key or self._api_key_provider or self.api_key, organization=organization or self.organization, project=project or self.project, webhook_secret=webhook_secret or self.webhook_secret, @@ -427,7 +442,7 @@ class AsyncOpenAI(AsyncAPIClient): def __init__( self, *, - api_key: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -465,7 +480,12 @@ def __init__( raise OpenAIError( "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" ) - self.api_key = api_key + if callable(api_key): + self.api_key = "" + self._api_key_provider: Callable[[], Awaitable[str]] | None = api_key + else: + self.api_key = api_key + self._api_key_provider = None if organization is None: organization = os.environ.get("OPENAI_ORG_ID") @@ -626,6 +646,15 @@ def with_streaming_response(self) -> AsyncOpenAIWithStreamedResponse: def qs(self) -> Querystring: return Querystring(array_format="brackets") + async def _refresh_api_key(self) -> None: + if self._api_key_provider: + self.api_key = await self._api_key_provider() + + @override + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + await self._refresh_api_key() + return await super()._prepare_options(options) + @property @override def auth_headers(self) -> dict[str, str]: @@ -649,7 +678,7 @@ def default_headers(self) -> dict[str, str | Omit]: def copy( self, *, - api_key: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -687,7 +716,7 @@ def copy( http_client = http_client or self._client return self.__class__( - api_key=api_key or self.api_key, + api_key=api_key or self._api_key_provider or self.api_key, organization=organization or self.organization, project=project or self.project, webhook_secret=webhook_secret or self.webhook_secret, diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index a994e4256c..ad64707261 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -94,7 +94,7 @@ def __init__( azure_endpoint: str, azure_deployment: str | None = None, api_version: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], str] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -114,7 +114,7 @@ def __init__( *, azure_deployment: str | None = None, api_version: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], str] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -134,7 +134,7 @@ def __init__( *, base_url: str, api_version: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], str] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -154,7 +154,7 @@ def __init__( api_version: str | None = None, azure_endpoint: str | None = None, azure_deployment: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], str] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -258,7 +258,7 @@ def __init__( def copy( self, *, - api_key: str | None = None, + api_key: str | Callable[[], str] | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -345,7 +345,7 @@ def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx.URL "api-version": self._api_version, "deployment": self._azure_deployment or model, } - if self.api_key != "": + if self.api_key and self.api_key != "": auth_headers = {"api-key": self.api_key} else: token = self._get_azure_ad_token() @@ -372,7 +372,7 @@ def __init__( azure_endpoint: str, azure_deployment: str | None = None, api_version: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -393,7 +393,7 @@ def __init__( *, azure_deployment: str | None = None, api_version: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -414,7 +414,7 @@ def __init__( *, base_url: str, api_version: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -435,7 +435,7 @@ def __init__( azure_endpoint: str | None = None, azure_deployment: str | None = None, api_version: str | None = None, - api_key: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -539,7 +539,7 @@ def __init__( def copy( self, *, - api_key: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -628,7 +628,7 @@ async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[htt "api-version": self._api_version, "deployment": self._azure_deployment or model, } - if self.api_key != "": + if self.api_key and self.api_key != "": auth_headers = {"api-key": self.api_key} else: token = await self._get_azure_ad_token() diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 7b99c7f6c4..4fa35963b6 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -358,6 +358,7 @@ async def __aenter__(self) -> AsyncRealtimeConnection: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc extra_query = self.__extra_query + await self.__client._refresh_api_key() auth_headers = self.__client.auth_headers if is_async_azure_client(self.__client): url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) @@ -540,6 +541,7 @@ def __enter__(self) -> RealtimeConnection: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc extra_query = self.__extra_query + self.__client._refresh_api_key() auth_headers = self.__client.auth_headers if is_azure_client(self.__client): url, auth_headers = self.__client._configure_realtime(self.__model, extra_query) diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index ebdfce86e3..2f5adf6548 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -326,6 +326,7 @@ async def __aenter__(self) -> AsyncRealtimeConnection: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc extra_query = self.__extra_query + await self.__client._refresh_api_key() auth_headers = self.__client.auth_headers if is_async_azure_client(self.__client): url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) @@ -507,6 +508,7 @@ def __enter__(self) -> RealtimeConnection: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc extra_query = self.__extra_query + self.__client._refresh_api_key() auth_headers = self.__client.auth_headers if is_azure_client(self.__client): url, auth_headers = self.__client._configure_realtime(self.__model, extra_query) diff --git a/tests/test_client.py b/tests/test_client.py index ccda50a7f0..e5300e55d7 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -11,7 +11,7 @@ import inspect import subprocess import tracemalloc -from typing import Any, Union, cast +from typing import Any, Union, Protocol, cast from textwrap import dedent from unittest import mock from typing_extensions import Literal @@ -41,6 +41,10 @@ api_key = "My API Key" +class MockRequestCall(Protocol): + request: httpx.Request + + def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -337,7 +341,9 @@ def test_default_headers_option(self) -> None: def test_validate_headers(self) -> None: client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + options = client._prepare_options(FinalRequestOptions(method="get", url="/foo")) + request = client._build_request(options) + assert request.headers.get("Authorization") == f"Bearer {api_key}" with pytest.raises(OpenAIError): @@ -939,6 +945,62 @@ def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + def test_api_key_before_after_refresh_provider(self) -> None: + client = OpenAI(base_url=base_url, api_key=lambda: "test_bearer_token") + + assert client.api_key == "" + assert "Authorization" not in client.auth_headers + + client._refresh_api_key() + + assert client.api_key == "test_bearer_token" + assert client.auth_headers.get("Authorization") == "Bearer test_bearer_token" + + def test_api_key_before_after_refresh_str(self) -> None: + client = OpenAI(base_url=base_url, api_key="test_api_key") + + assert client.auth_headers.get("Authorization") == "Bearer test_api_key" + client._refresh_api_key() + + assert client.auth_headers.get("Authorization") == "Bearer test_api_key" + + @pytest.mark.respx() + def test_api_key_refresh_on_retry(self, respx_mock: MockRouter) -> None: + respx_mock.post(base_url + "/chat/completions").mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response(200, json={"foo": "bar"}), + ] + ) + + counter = 0 + + def token_provider() -> str: + nonlocal counter + + counter += 1 + + if counter == 1: + return "first" + + return "second" + + client = OpenAI(base_url=base_url, api_key=token_provider) + client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 2 + + assert calls[0].request.headers.get("Authorization") == "Bearer first" + assert calls[1].request.headers.get("Authorization") == "Bearer second" + + def test_copy_auth(self) -> None: + client = OpenAI(base_url=base_url, api_key=lambda: "test_bearer_token_1").copy( + api_key=lambda: "test_bearer_token_2" + ) + client._refresh_api_key() + assert client.auth_headers == {"Authorization": "Bearer test_bearer_token_2"} + class TestAsyncOpenAI: client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) @@ -1220,9 +1282,10 @@ def test_default_headers_option(self) -> None: assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" - def test_validate_headers(self) -> None: + async def test_validate_headers(self) -> None: client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + options = await client._prepare_options(FinalRequestOptions(method="get", url="/foo")) + request = client._build_request(options) assert request.headers.get("Authorization") == f"Bearer {api_key}" with pytest.raises(OpenAIError): @@ -1887,3 +1950,70 @@ async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + + @pytest.mark.asyncio + async def test_api_key_before_after_refresh_provider(self) -> None: + async def mock_api_key_provider(): + return "test_bearer_token" + + client = AsyncOpenAI(base_url=base_url, api_key=mock_api_key_provider) + + assert client.api_key == "" + assert "Authorization" not in client.auth_headers + + await client._refresh_api_key() + + assert client.api_key == "test_bearer_token" + assert client.auth_headers.get("Authorization") == "Bearer test_bearer_token" + + @pytest.mark.asyncio + async def test_api_key_before_after_refresh_str(self) -> None: + client = AsyncOpenAI(base_url=base_url, api_key="test_api_key") + + assert client.auth_headers.get("Authorization") == "Bearer test_api_key" + await client._refresh_api_key() + + assert client.auth_headers.get("Authorization") == "Bearer test_api_key" + + @pytest.mark.asyncio + @pytest.mark.respx() + async def test_bearer_token_refresh_async(self, respx_mock: MockRouter) -> None: + respx_mock.post(base_url + "/chat/completions").mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response(200, json={"foo": "bar"}), + ] + ) + + counter = 0 + + async def token_provider() -> str: + nonlocal counter + + counter += 1 + + if counter == 1: + return "first" + + return "second" + + client = AsyncOpenAI(base_url=base_url, api_key=token_provider) + await client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 2 + + assert calls[0].request.headers.get("Authorization") == "Bearer first" + assert calls[1].request.headers.get("Authorization") == "Bearer second" + + @pytest.mark.asyncio + async def test_copy_auth(self) -> None: + async def token_provider_1() -> str: + return "test_bearer_token_1" + + async def token_provider_2() -> str: + return "test_bearer_token_2" + + client = AsyncOpenAI(base_url=base_url, api_key=token_provider_1).copy(api_key=token_provider_2) + await client._refresh_api_key() + assert client.auth_headers == {"Authorization": "Bearer test_bearer_token_2"} From 2cf4ed5072f89103c674a61d22879b06a4c407f6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:05:40 +0000 Subject: [PATCH 099/408] feat: improve future compat with pydantic v3 --- src/openai/_base_client.py | 6 +- src/openai/_compat.py | 108 +++++++++---------- src/openai/_models.py | 82 +++++++------- src/openai/_utils/__init__.py | 10 +- src/openai/_utils/_compat.py | 45 ++++++++ src/openai/_utils/_datetime_parse.py | 136 ++++++++++++++++++++++++ src/openai/_utils/_transform.py | 6 +- src/openai/_utils/_typing.py | 2 +- src/openai/_utils/_utils.py | 1 - src/openai/cli/_cli.py | 12 +-- src/openai/cli/_models.py | 8 +- src/openai/lib/_parsing/_completions.py | 4 +- src/openai/lib/_parsing/_responses.py | 4 +- src/openai/lib/_pydantic.py | 4 +- tests/lib/chat/test_completions.py | 6 +- tests/lib/test_pydantic.py | 8 +- tests/test_models.py | 48 ++++----- tests/test_transform.py | 16 +-- tests/test_utils/test_datetime_parse.py | 110 +++++++++++++++++++ tests/utils.py | 8 +- 20 files changed, 462 insertions(+), 162 deletions(-) create mode 100644 src/openai/_utils/_compat.py create mode 100644 src/openai/_utils/_datetime_parse.py create mode 100644 tests/test_utils/test_datetime_parse.py diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index f71e00f51f..d5f1ab0903 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -59,7 +59,7 @@ ModelBuilderProtocol, ) from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import PYDANTIC_V2, model_copy, model_dump +from ._compat import PYDANTIC_V1, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -234,7 +234,7 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: - if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model @@ -322,7 +322,7 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: - if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 87fc370765..73a1f3ea93 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -12,14 +12,13 @@ _T = TypeVar("_T") _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) -# --------------- Pydantic v2 compatibility --------------- +# --------------- Pydantic v2, v3 compatibility --------------- # Pyright incorrectly reports some of our functions as overriding a method when they don't # pyright: reportIncompatibleMethodOverride=false -PYDANTIC_V2 = pydantic.VERSION.startswith("2.") +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") -# v1 re-exports if TYPE_CHECKING: def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 @@ -44,90 +43,92 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 ... else: - if PYDANTIC_V2: - from pydantic.v1.typing import ( + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( get_args as get_args, is_union as is_union, get_origin as get_origin, is_typeddict as is_typeddict, is_literal_type as is_literal_type, ) - from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime else: - from pydantic.typing import ( + from ._utils import ( get_args as get_args, is_union as is_union, get_origin as get_origin, + parse_date as parse_date, is_typeddict as is_typeddict, + parse_datetime as parse_datetime, is_literal_type as is_literal_type, ) - from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # refactored config if TYPE_CHECKING: from pydantic import ConfigDict as ConfigDict else: - if PYDANTIC_V2: - from pydantic import ConfigDict - else: + if PYDANTIC_V1: # TODO: provide an error message here? ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict # renamed methods / properties def parse_obj(model: type[_ModelT], value: object) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(value) - else: + if PYDANTIC_V1: return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) def field_is_required(field: FieldInfo) -> bool: - if PYDANTIC_V2: - return field.is_required() - return field.required # type: ignore + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() def field_get_default(field: FieldInfo) -> Any: value = field.get_default() - if PYDANTIC_V2: - from pydantic_core import PydanticUndefined - - if value == PydanticUndefined: - return None + if PYDANTIC_V1: return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None return value def field_outer_type(field: FieldInfo) -> Any: - if PYDANTIC_V2: - return field.annotation - return field.outer_type_ # type: ignore + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation def get_model_config(model: type[pydantic.BaseModel]) -> Any: - if PYDANTIC_V2: - return model.model_config - return model.__config__ # type: ignore + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: - if PYDANTIC_V2: - return model.model_fields - return model.__fields__ # type: ignore + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: - if PYDANTIC_V2: - return model.model_copy(deep=deep) - return model.copy(deep=deep) # type: ignore + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: - if PYDANTIC_V2: - return model.model_dump_json(indent=indent) - return model.json(indent=indent) # type: ignore + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) def model_dump( @@ -139,14 +140,14 @@ def model_dump( warnings: bool = True, mode: Literal["json", "python"] = "python", ) -> dict[str, Any]: - if PYDANTIC_V2 or hasattr(model, "model_dump"): + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): return model.model_dump( mode=mode, exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 - warnings=warnings if PYDANTIC_V2 else True, + warnings=True if PYDANTIC_V1 else warnings, ) return cast( "dict[str, Any]", @@ -159,21 +160,21 @@ def model_dump( def model_parse(model: type[_ModelT], data: Any) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(data) - return model.parse_obj(data) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) def model_parse_json(model: type[_ModelT], data: str | bytes) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate_json(data) - return model.parse_raw(data) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return model.parse_raw(data) # pyright: ignore[reportDeprecated] + return model.model_validate_json(data) def model_json_schema(model: type[_ModelT]) -> dict[str, Any]: - if PYDANTIC_V2: - return model.model_json_schema() - return model.schema() # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return model.schema() # pyright: ignore[reportDeprecated] + return model.model_json_schema() # generic models @@ -182,17 +183,16 @@ def model_json_schema(model: type[_ModelT]) -> dict[str, Any]: class GenericModel(pydantic.BaseModel): ... else: - if PYDANTIC_V2: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: # there no longer needs to be a distinction in v2 but # we still have to create our own subclass to avoid # inconsistent MRO ordering errors class GenericModel(pydantic.BaseModel): ... - else: - import pydantic.generics - - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... - # cached properties if TYPE_CHECKING: diff --git a/src/openai/_models.py b/src/openai/_models.py index 50eb0af751..8ee8612d1e 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -51,7 +51,7 @@ strip_annotated_type, ) from ._compat import ( - PYDANTIC_V2, + PYDANTIC_V1, ConfigDict, GenericModel as BaseGenericModel, get_args, @@ -84,11 +84,7 @@ class _ConfigProtocol(Protocol): class BaseModel(pydantic.BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) - ) - else: + if PYDANTIC_V1: @property @override @@ -103,6 +99,10 @@ class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] def __repr_args__(self) -> ReprArgs: # we don't want these attributes to be included when something like `rich.print` is used return [arg for arg in super().__repr_args__() if arg[0] not in {"_request_id", "__exclude_fields__"}] + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) if TYPE_CHECKING: _request_id: Optional[str] = None @@ -240,25 +240,25 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] if key not in model_fields: parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value - if PYDANTIC_V2: - _extra[key] = parsed - else: + if PYDANTIC_V1: _fields_set.add(key) fields_values[key] = parsed + else: + _extra[key] = parsed object.__setattr__(m, "__dict__", fields_values) - if PYDANTIC_V2: - # these properties are copied from Pydantic's `model_construct()` method - object.__setattr__(m, "__pydantic_private__", None) - object.__setattr__(m, "__pydantic_extra__", _extra) - object.__setattr__(m, "__pydantic_fields_set__", _fields_set) - else: + if PYDANTIC_V1: # init_private_attributes() does not exist in v2 m._init_private_attributes() # type: ignore # copied from Pydantic v1's `construct()` method object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) return m @@ -268,7 +268,7 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] # although not in practice model_construct = construct - if not PYDANTIC_V2: + if PYDANTIC_V1: # we define aliases for some of the new pydantic v2 methods so # that we can just document these methods without having to specify # a specific pydantic version as some users may not know which @@ -388,10 +388,10 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) - if PYDANTIC_V2: - type_ = field.annotation - else: + if PYDANTIC_V1: type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore if type_ is None: raise RuntimeError(f"Unexpected field type is None for {key}") @@ -400,7 +400,7 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: - if not PYDANTIC_V2: + if PYDANTIC_V1: # TODO return None @@ -653,30 +653,30 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, for variant in get_args(union): variant = strip_annotated_type(variant) if is_basemodel_type(variant): - if PYDANTIC_V2: - field = _extract_field_schema_pv2(variant, discriminator_field_name) - if not field: + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field.get("serialization_alias") - - field_schema = field["schema"] + discriminator_alias = field_info.alias - if field_schema["type"] == "literal": - for entry in cast("LiteralSchema", field_schema)["expected"]: + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): if isinstance(entry, str): mapping[entry] = variant else: - field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - if not field_info: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field_info.alias + discriminator_alias = field.get("serialization_alias") - if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): - for entry in get_args(annotation): + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: if isinstance(entry, str): mapping[entry] = variant @@ -735,7 +735,7 @@ def add_request_id(obj: BaseModel, request_id: str | None) -> None: # in Pydantic v1, using setattr like we do above causes the attribute # to be included when serializing the model which we don't want in this # case so we need to explicitly exclude it - if not PYDANTIC_V2: + if PYDANTIC_V1: try: exclude_fields = obj.__exclude_fields__ # type: ignore except AttributeError: @@ -754,7 +754,7 @@ class GenericModel(BaseGenericModel, BaseModel): pass -if PYDANTIC_V2: +if not PYDANTIC_V1: from pydantic import TypeAdapter as _TypeAdapter _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) @@ -822,12 +822,12 @@ class FinalRequestOptions(pydantic.BaseModel): json_data: Union[Body, None] = None extra_json: Union[AnyMapping, None] = None - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) - else: + if PYDANTIC_V1: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) def get_max_retries(self, max_retries: int) -> int: if isinstance(self.max_retries, NotGiven): @@ -860,9 +860,9 @@ def construct( # type: ignore key: strip_not_given(value) for key, value in values.items() } - if PYDANTIC_V2: - return super().model_construct(_fields_set, **kwargs) - return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) if not TYPE_CHECKING: # type checkers incorrectly complain about this assignment diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 6471aa4c0d..963c83b6d4 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -11,7 +11,6 @@ lru_cache as lru_cache, is_mapping as is_mapping, is_tuple_t as is_tuple_t, - parse_date as parse_date, is_iterable as is_iterable, is_sequence as is_sequence, coerce_float as coerce_float, @@ -24,7 +23,6 @@ coerce_boolean as coerce_boolean, coerce_integer as coerce_integer, file_from_path as file_from_path, - parse_datetime as parse_datetime, is_azure_client as is_azure_client, strip_not_given as strip_not_given, deepcopy_minimal as deepcopy_minimal, @@ -35,6 +33,13 @@ maybe_coerce_integer as maybe_coerce_integer, is_async_azure_client as is_async_azure_client, ) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) from ._typing import ( is_list_type as is_list_type, is_union_type as is_union_type, @@ -59,3 +64,4 @@ function_has_argument as function_has_argument, assert_signatures_in_sync as assert_signatures_in_sync, ) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/src/openai/_utils/_compat.py b/src/openai/_utils/_compat.py new file mode 100644 index 0000000000..dd703233c5 --- /dev/null +++ b/src/openai/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/src/openai/_utils/_datetime_parse.py b/src/openai/_utils/_datetime_parse.py new file mode 100644 index 0000000000..7cb9d9e668 --- /dev/null +++ b/src/openai/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index f5c41c09c4..bc262ea339 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -19,6 +19,7 @@ is_sequence, ) from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict from ._typing import ( is_list_type, is_union_type, @@ -29,7 +30,6 @@ is_annotated_type, strip_annotated_type, ) -from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -169,6 +169,8 @@ def _transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation @@ -333,6 +335,8 @@ async def _async_transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation diff --git a/src/openai/_utils/_typing.py b/src/openai/_utils/_typing.py index 845cd6b287..193109f3ad 100644 --- a/src/openai/_utils/_typing.py +++ b/src/openai/_utils/_typing.py @@ -15,7 +15,7 @@ from ._utils import lru_cache from .._types import InheritsGeneric -from .._compat import is_union as _is_union +from ._compat import is_union as _is_union def is_annotated_type(typ: type) -> bool: diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 1e7d013b51..4a23c96c0a 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -23,7 +23,6 @@ import sniffio from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import parse_date as parse_date, parse_datetime as parse_datetime _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) diff --git a/src/openai/cli/_cli.py b/src/openai/cli/_cli.py index fd165f48ab..d31196da50 100644 --- a/src/openai/cli/_cli.py +++ b/src/openai/cli/_cli.py @@ -16,7 +16,7 @@ from ._api import register_commands from ._utils import can_use_http2 from ._errors import CLIError, display_error -from .._compat import PYDANTIC_V2, ConfigDict, model_parse +from .._compat import PYDANTIC_V1, ConfigDict, model_parse from .._models import BaseModel from .._exceptions import APIError @@ -28,14 +28,14 @@ class Arguments(BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="ignore", - ) - else: + if PYDANTIC_V1: class Config(pydantic.BaseConfig): # type: ignore extra: Any = pydantic.Extra.ignore # type: ignore + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="ignore", + ) verbosity: int version: Optional[str] = None diff --git a/src/openai/cli/_models.py b/src/openai/cli/_models.py index 5583db2609..a88608961b 100644 --- a/src/openai/cli/_models.py +++ b/src/openai/cli/_models.py @@ -4,14 +4,14 @@ import pydantic from .. import _models -from .._compat import PYDANTIC_V2, ConfigDict +from .._compat import PYDANTIC_V1, ConfigDict class BaseModel(_models.BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore", arbitrary_types_allowed=True) - else: + if PYDANTIC_V1: class Config(pydantic.BaseConfig): # type: ignore extra: Any = pydantic.Extra.ignore # type: ignore arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore", arbitrary_types_allowed=True) diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index fc0bd05e4d..4b8b78b70a 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -10,7 +10,7 @@ from .._tools import PydanticFunctionTool from ..._types import NOT_GIVEN, NotGiven from ..._utils import is_dict, is_given -from ..._compat import PYDANTIC_V2, model_parse_json +from ..._compat import PYDANTIC_V1, model_parse_json from ..._models import construct_type_unchecked from .._pydantic import is_basemodel_type, to_strict_json_schema, is_dataclass_like_type from ...types.chat import ( @@ -262,7 +262,7 @@ def _parse_content(response_format: type[ResponseFormatT], content: str) -> Resp return cast(ResponseFormatT, model_parse_json(response_format, content)) if is_dataclass_like_type(response_format): - if not PYDANTIC_V2: + if PYDANTIC_V1: raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {response_format}") return pydantic.TypeAdapter(response_format).validate_json(content) diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 2a30ac836c..b6ebde0e8e 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -9,7 +9,7 @@ from .._tools import ResponsesPydanticFunctionTool from ..._types import NotGiven from ..._utils import is_given -from ..._compat import PYDANTIC_V2, model_parse_json +from ..._compat import PYDANTIC_V1, model_parse_json from ..._models import construct_type_unchecked from .._pydantic import is_basemodel_type, is_dataclass_like_type from ._completions import solve_response_format_t, type_to_response_format_param @@ -138,7 +138,7 @@ def parse_text(text: str, text_format: type[TextFormatT] | NotGiven) -> TextForm return cast(TextFormatT, model_parse_json(text_format, text)) if is_dataclass_like_type(text_format): - if not PYDANTIC_V2: + if PYDANTIC_V1: raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {text_format}") return pydantic.TypeAdapter(text_format).validate_json(text) diff --git a/src/openai/lib/_pydantic.py b/src/openai/lib/_pydantic.py index c2d73e5fc6..3cfe224cb1 100644 --- a/src/openai/lib/_pydantic.py +++ b/src/openai/lib/_pydantic.py @@ -8,7 +8,7 @@ from .._types import NOT_GIVEN from .._utils import is_dict as _is_dict, is_list -from .._compat import PYDANTIC_V2, model_json_schema +from .._compat import PYDANTIC_V1, model_json_schema _T = TypeVar("_T") @@ -16,7 +16,7 @@ def to_strict_json_schema(model: type[pydantic.BaseModel] | pydantic.TypeAdapter[Any]) -> dict[str, Any]: if inspect.isclass(model) and is_basemodel_type(model): schema = model_json_schema(model) - elif PYDANTIC_V2 and isinstance(model, pydantic.TypeAdapter): + elif (not PYDANTIC_V1) and isinstance(model, pydantic.TypeAdapter): schema = model.json_schema() else: raise TypeError(f"Non BaseModel types are only supported with Pydantic v2 - {model}") diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index f69bc09ca3..afad5a1391 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -12,7 +12,7 @@ import openai from openai import OpenAI, AsyncOpenAI from openai._utils import assert_signatures_in_sync -from openai._compat import PYDANTIC_V2 +from openai._compat import PYDANTIC_V1 from ..utils import print_obj from ...conftest import base_url @@ -245,7 +245,7 @@ class ColorDetection(BaseModel): color: Color hex_color_code: str = Field(description="The hex color code of the detected color") - if not PYDANTIC_V2: + if PYDANTIC_V1: ColorDetection.update_forward_refs(**locals()) # type: ignore completion = make_snapshot_request( @@ -368,7 +368,7 @@ class Location(BaseModel): @pytest.mark.respx(base_url=base_url) -@pytest.mark.skipif(not PYDANTIC_V2, reason="dataclasses only supported in v2") +@pytest.mark.skipif(PYDANTIC_V1, reason="dataclasses only supported in v2") def test_parse_pydantic_dataclass(client: OpenAI, respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: from pydantic.dataclasses import dataclass diff --git a/tests/lib/test_pydantic.py b/tests/lib/test_pydantic.py index 7e128b70c0..754a15151c 100644 --- a/tests/lib/test_pydantic.py +++ b/tests/lib/test_pydantic.py @@ -6,14 +6,14 @@ from inline_snapshot import snapshot import openai -from openai._compat import PYDANTIC_V2 +from openai._compat import PYDANTIC_V1 from openai.lib._pydantic import to_strict_json_schema from .schema_types.query import Query def test_most_types() -> None: - if PYDANTIC_V2: + if not PYDANTIC_V1: assert openai.pydantic_function_tool(Query)["function"] == snapshot( { "name": "Query", @@ -181,7 +181,7 @@ class ColorDetection(BaseModel): def test_enums() -> None: - if PYDANTIC_V2: + if not PYDANTIC_V1: assert openai.pydantic_function_tool(ColorDetection)["function"] == snapshot( { "name": "ColorDetection", @@ -253,7 +253,7 @@ class Universe(BaseModel): def test_nested_inline_ref_expansion() -> None: - if PYDANTIC_V2: + if not PYDANTIC_V1: assert to_strict_json_schema(Universe) == snapshot( { "title": "Universe", diff --git a/tests/test_models.py b/tests/test_models.py index 54a3a32048..410ec3bf4e 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -8,7 +8,7 @@ from pydantic import Field from openai._utils import PropertyInfo -from openai._compat import PYDANTIC_V2, parse_obj, model_dump, model_json +from openai._compat import PYDANTIC_V1, parse_obj, model_dump, model_json from openai._models import BaseModel, construct_type @@ -294,12 +294,12 @@ class Model(BaseModel): assert cast(bool, m.foo) is True m = Model.construct(foo={"name": 3}) - if PYDANTIC_V2: - assert isinstance(m.foo, Submodel1) - assert m.foo.name == 3 # type: ignore - else: + if PYDANTIC_V1: assert isinstance(m.foo, Submodel2) assert m.foo.name == "3" + else: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore def test_list_of_unions() -> None: @@ -426,10 +426,10 @@ class Model(BaseModel): expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) - if PYDANTIC_V2: - expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' - else: + if PYDANTIC_V1: expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + else: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' model = Model.construct(created_at="2019-12-27T18:11:19.117Z") assert model.created_at == expected @@ -531,7 +531,7 @@ class Model2(BaseModel): assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} assert m4.to_dict(mode="json") == {"created_at": time_str} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_dict(warnings=False) @@ -556,7 +556,7 @@ class Model(BaseModel): assert m3.model_dump() == {"foo": None} assert m3.model_dump(exclude_none=True) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump(round_trip=True) @@ -580,10 +580,10 @@ class Model(BaseModel): assert json.loads(m.to_json()) == {"FOO": "hello"} assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} - if PYDANTIC_V2: - assert m.to_json(indent=None) == '{"FOO":"hello"}' - else: + if PYDANTIC_V1: assert m.to_json(indent=None) == '{"FOO": "hello"}' + else: + assert m.to_json(indent=None) == '{"FOO":"hello"}' m2 = Model() assert json.loads(m2.to_json()) == {} @@ -595,7 +595,7 @@ class Model(BaseModel): assert json.loads(m3.to_json()) == {"FOO": None} assert json.loads(m3.to_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_json(warnings=False) @@ -622,7 +622,7 @@ class Model(BaseModel): assert json.loads(m3.model_dump_json()) == {"foo": None} assert json.loads(m3.model_dump_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump_json(round_trip=True) @@ -679,12 +679,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_unknown_variant() -> None: @@ -768,12 +768,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.foo_type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: @@ -833,7 +833,7 @@ class B(BaseModel): assert UnionType.__discriminator__ is discriminator -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_type_alias_type() -> None: Alias = TypeAliasType("Alias", str) # pyright: ignore @@ -849,7 +849,7 @@ class Model(BaseModel): assert m.union == "bar" -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_field_named_cls() -> None: class Model(BaseModel): cls: str @@ -936,7 +936,7 @@ class Type2(BaseModel): assert isinstance(model.value, InnerType2) -@pytest.mark.skipif(not PYDANTIC_V2, reason="this is only supported in pydantic v2 for now") +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now") def test_extra_properties() -> None: class Item(BaseModel): prop: int diff --git a/tests/test_transform.py b/tests/test_transform.py index 965f65f74f..036cfdfb06 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -15,7 +15,7 @@ parse_datetime, async_transform as _async_transform, ) -from openai._compat import PYDANTIC_V2 +from openai._compat import PYDANTIC_V1 from openai._models import BaseModel _T = TypeVar("_T") @@ -189,7 +189,7 @@ class DateModel(BaseModel): @pytest.mark.asyncio async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - tz = "Z" if PYDANTIC_V2 else "+00:00" + tz = "+00:00" if PYDANTIC_V1 else "Z" assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] @@ -297,11 +297,11 @@ async def test_pydantic_unknown_field(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_types(use_async: bool) -> None: model = MyModel.construct(foo=True) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": True} @@ -309,11 +309,11 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_object_type(use_async: bool) -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": {"hello": "world"}} diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py new file mode 100644 index 0000000000..44c33a4ccb --- /dev/null +++ b/tests/test_utils/test_datetime_parse.py @@ -0,0 +1,110 @@ +""" +Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py +with modifications so it works without pydantic v1 imports. +""" + +from typing import Type, Union +from datetime import date, datetime, timezone, timedelta + +import pytest + +from openai._utils import parse_date, parse_datetime + + +def create_tz(minutes: int) -> timezone: + return timezone(timedelta(minutes=minutes)) + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + ("1494012444.883309", date(2017, 5, 5)), + (b"1494012444.883309", date(2017, 5, 5)), + (1_494_012_444.883_309, date(2017, 5, 5)), + ("1494012444", date(2017, 5, 5)), + (1_494_012_444, date(2017, 5, 5)), + (0, date(1970, 1, 1)), + ("2012-04-23", date(2012, 4, 23)), + (b"2012-04-23", date(2012, 4, 23)), + ("2012-4-9", date(2012, 4, 9)), + (date(2012, 4, 9), date(2012, 4, 9)), + (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)), + # Invalid inputs + ("x20120423", ValueError), + ("2012-04-56", ValueError), + (19_999_999_999, date(2603, 10, 11)), # just before watershed + (20_000_000_001, date(1970, 8, 20)), # just after watershed + (1_549_316_052, date(2019, 2, 4)), # nowish in s + (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms + (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs + (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns + ("infinity", date(9999, 12, 31)), + ("inf", date(9999, 12, 31)), + (float("inf"), date(9999, 12, 31)), + ("infinity ", date(9999, 12, 31)), + (int("1" + "0" * 100), date(9999, 12, 31)), + (1e1000, date(9999, 12, 31)), + ("-infinity", date(1, 1, 1)), + ("-inf", date(1, 1, 1)), + ("nan", ValueError), + ], +) +def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_date(value) + else: + assert parse_date(value) == result + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + # values in seconds + ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + # values in ms + ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)), + ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)), + (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)), + ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)), + ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)), + ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))), + ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))), + ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))), + ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (datetime(2017, 5, 5), datetime(2017, 5, 5)), + (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)), + # Invalid inputs + ("x20120423091500", ValueError), + ("2012-04-56T09:15:90", ValueError), + ("2012-04-23T11:05:00-25:00", ValueError), + (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed + (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed + (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s + (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms + (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs + (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns + ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)), + (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)), + (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("-infinity", datetime(1, 1, 1, 0, 0)), + ("-inf", datetime(1, 1, 1, 0, 0)), + ("nan", ValueError), + ], +) +def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_datetime(value) + else: + assert parse_datetime(value) == result diff --git a/tests/utils.py b/tests/utils.py index a07052140b..e03ed1a039 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -22,7 +22,7 @@ is_annotated_type, is_type_alias_type, ) -from openai._compat import PYDANTIC_V2, field_outer_type, get_model_fields +from openai._compat import PYDANTIC_V1, field_outer_type, get_model_fields from openai._models import BaseModel BaseModelT = TypeVar("BaseModelT", bound=BaseModel) @@ -35,12 +35,12 @@ def evaluate_forwardref(forwardref: ForwardRef, globalns: dict[str, Any]) -> typ def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: for name, field in get_model_fields(model).items(): field_value = getattr(value, name) - if PYDANTIC_V2: - allow_none = False - else: + if PYDANTIC_V1: # in v1 nullability was structured differently # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields allow_none = getattr(field, "allow_none", False) + else: + allow_none = False assert_matches_type( field_outer_type(field), From 2de8d7cde5565ec71851d8bc3a26f021cebab32c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 13:34:31 +0000 Subject: [PATCH 100/408] release: 1.106.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1e15251d64..3064ce9554 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.105.0" + ".": "1.106.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ed3bbe6ed..423ace20c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.106.0 (2025-09-04) + +Full Changelog: [v1.105.0...v1.106.0](https://github.com/openai/openai-python/compare/v1.105.0...v1.106.0) + +### Features + +* **client:** support callable api_key ([#2588](https://github.com/openai/openai-python/issues/2588)) ([e1bad01](https://github.com/openai/openai-python/commit/e1bad015b8a2b98bfee955a24bc931347a58efc1)) +* improve future compat with pydantic v3 ([6645d93](https://github.com/openai/openai-python/commit/6645d9317a240982928b92c2f4af0381db6edc09)) + ## 1.105.0 (2025-09-03) Full Changelog: [v1.104.2...v1.105.0](https://github.com/openai/openai-python/compare/v1.104.2...v1.105.0) diff --git a/pyproject.toml b/pyproject.toml index 587ca41e01..94d85b8442 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.105.0" +version = "1.106.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5509cd4d8e..6a8d9a3e2d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.105.0" # x-release-please-version +__version__ = "1.106.0" # x-release-please-version From c4f9d0b997e18614709752e030f85d9e8281b4e0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:54:58 +0000 Subject: [PATCH 101/408] chore(internal): move mypy configurations to `pyproject.toml` file --- mypy.ini | 53 ------------------------------------------- pyproject.toml | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 53 deletions(-) delete mode 100644 mypy.ini diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 660f1a086e..0000000000 --- a/mypy.ini +++ /dev/null @@ -1,53 +0,0 @@ -[mypy] -pretty = True -show_error_codes = True - -# Exclude _files.py and _logs.py because mypy isn't smart enough to apply -# the correct type narrowing and as this is an internal module -# it's fine to just use Pyright. -# -# We also exclude our `tests` as mypy doesn't always infer -# types correctly and Pyright will still catch any type errors. - -# realtime examples use inline `uv` script dependencies -# which means it can't be type checked -exclude = ^(src/openai/_files\.py|_dev/.*\.py|tests/.*|src/openai/_utils/_logs\.py|examples/realtime/audio_util\.py|examples/realtime/push_to_talk_app\.py)$ - -strict_equality = True -implicit_reexport = True -check_untyped_defs = True -no_implicit_optional = True - -warn_return_any = True -warn_unreachable = True -warn_unused_configs = True - -# Turn these options off as it could cause conflicts -# with the Pyright options. -warn_unused_ignores = False -warn_redundant_casts = False - -disallow_any_generics = True -disallow_untyped_defs = True -disallow_untyped_calls = True -disallow_subclassing_any = True -disallow_incomplete_defs = True -disallow_untyped_decorators = True -cache_fine_grained = True - -# By default, mypy reports an error if you assign a value to the result -# of a function call that doesn't return anything. We do this in our test -# cases: -# ``` -# result = ... -# assert result is None -# ``` -# Changing this codegen to make mypy happy would increase complexity -# and would not be worth it. -disable_error_code = func-returns-value,overload-cannot-match - -# https://github.com/python/mypy/issues/12162 -[mypy.overrides] -module = "black.files.*" -ignore_errors = true -ignore_missing_imports = true diff --git a/pyproject.toml b/pyproject.toml index 94d85b8442..c420bfa1e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -179,6 +179,67 @@ reportOverlappingOverload = false reportImportCycles = false reportPrivateUsage = false +[tool.mypy] +pretty = true +show_error_codes = true + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +# +# realtime examples use inline `uv` script dependencies +# which means it can't be type checked +exclude = [ + 'src/openai/_files.py', + '_dev/.*.py', + 'tests/.*', + 'src/openai/_utils/_logs.py', + 'examples/realtime/audio_util.py', + 'examples/realtime/push_to_talk_app.py', +] + +strict_equality = true +implicit_reexport = true +check_untyped_defs = true +no_implicit_optional = true + +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = false +warn_redundant_casts = false + +disallow_any_generics = true +disallow_untyped_defs = true +disallow_untyped_calls = true +disallow_subclassing_any = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +cache_fine_grained = true + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = "func-returns-value,overload-cannot-match" + +# https://github.com/python/mypy/issues/12162 +[[tool.mypy.overrides]] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true + [tool.ruff] line-length = 120 output-format = "grouped" From 2adf11112988e998fcf5adb805bae38501d22318 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:55:32 +0000 Subject: [PATCH 102/408] release: 1.106.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3064ce9554..f2761d4022 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.106.0" + ".": "1.106.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 423ace20c9..c0ad7d1490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.106.1 (2025-09-04) + +Full Changelog: [v1.106.0...v1.106.1](https://github.com/openai/openai-python/compare/v1.106.0...v1.106.1) + +### Chores + +* **internal:** move mypy configurations to `pyproject.toml` file ([ca413a2](https://github.com/openai/openai-python/commit/ca413a277496c3b883b103ad1138a886e89ae15e)) + ## 1.106.0 (2025-09-04) Full Changelog: [v1.105.0...v1.106.0](https://github.com/openai/openai-python/compare/v1.105.0...v1.106.0) diff --git a/pyproject.toml b/pyproject.toml index c420bfa1e3..82aa72b045 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.106.0" +version = "1.106.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6a8d9a3e2d..33c16fef6a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.106.0" # x-release-please-version +__version__ = "1.106.1" # x-release-please-version From 0296375f9cf45ed3786292a0c03bb52a2ca06b94 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:24:53 -0400 Subject: [PATCH 103/408] release: 1.107.0 (#2613) * chore(internal): codegen related update * feat(api): ship the RealtimeGA API shape Updates types to use the GA shape for Realtime API * release: 1.107.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +- CHANGELOG.md | 13 + api.md | 25 +- pyproject.toml | 2 +- requirements-dev.lock | 2 +- src/openai/_version.py | 2 +- .../resources/realtime/client_secrets.py | 16 +- src/openai/resources/realtime/realtime.py | 129 +++--- src/openai/types/realtime/__init__.py | 65 +++- .../types/realtime/audio_transcription.py | 36 ++ .../realtime/audio_transcription_param.py | 33 ++ .../realtime/client_secret_create_params.py | 17 +- .../realtime/client_secret_create_response.py | 97 +---- ...put_audio_transcription_completed_event.py | 7 +- ...m_input_audio_transcription_delta_event.py | 11 +- .../conversation_item_truncate_event.py | 2 +- .../conversation_item_truncate_event_param.py | 2 +- src/openai/types/realtime/models.py | 25 ++ src/openai/types/realtime/models_param.py | 24 ++ .../types/realtime/noise_reduction_type.py | 7 + .../types/realtime/realtime_audio_config.py | 181 +-------- .../realtime/realtime_audio_config_input.py | 60 +++ .../realtime_audio_config_input_param.py | 61 +++ .../realtime/realtime_audio_config_output.py | 36 ++ .../realtime_audio_config_output_param.py | 35 ++ .../realtime/realtime_audio_config_param.py | 183 +-------- .../types/realtime/realtime_audio_formats.py | 30 ++ .../realtime/realtime_audio_formats_param.py | 29 ++ .../realtime_audio_input_turn_detection.py | 64 +++ ...altime_audio_input_turn_detection_param.py | 64 +++ .../realtime/realtime_client_secret_config.py | 27 -- .../realtime_client_secret_config_param.py | 26 -- ...ime_conversation_item_assistant_message.py | 30 +- ...nversation_item_assistant_message_param.py | 30 +- ...ealtime_conversation_item_function_call.py | 16 +- ..._conversation_item_function_call_output.py | 15 +- ...rsation_item_function_call_output_param.py | 15 +- ...e_conversation_item_function_call_param.py | 16 +- ...altime_conversation_item_system_message.py | 10 +- ..._conversation_item_system_message_param.py | 10 +- ...realtime_conversation_item_user_message.py | 39 +- ...me_conversation_item_user_message_param.py | 39 +- .../types/realtime/realtime_response.py | 59 +-- .../realtime_response_create_audio_output.py | 29 ++ ...time_response_create_audio_output_param.py | 28 ++ .../realtime_response_create_mcp_tool.py | 135 +++++++ ...realtime_response_create_mcp_tool_param.py | 135 +++++++ .../realtime_response_create_params.py | 98 +++++ .../realtime_response_create_params_param.py | 99 +++++ .../types/realtime/realtime_response_usage.py | 8 +- ...time_response_usage_input_token_details.py | 25 +- src/openai/types/realtime/realtime_session.py | 307 --------------- .../realtime_session_client_secret.py | 20 + .../realtime_session_create_request.py | 63 ++- .../realtime_session_create_request_param.py | 64 ++- .../realtime_session_create_response.py | 368 ++++++++++++++---- .../realtime/realtime_tools_config_param.py | 21 +- .../realtime/realtime_tools_config_union.py | 21 +- .../realtime_tools_config_union_param.py | 21 +- .../types/realtime/realtime_tracing_config.py | 8 +- .../realtime/realtime_tracing_config_param.py | 8 +- .../realtime_transcription_session_audio.py | 12 + ...ltime_transcription_session_audio_input.py | 62 +++ ...transcription_session_audio_input_param.py | 63 +++ ...tion_session_audio_input_turn_detection.py | 63 +++ ...ession_audio_input_turn_detection_param.py | 63 +++ ...ltime_transcription_session_audio_param.py | 13 + ...ime_transcription_session_client_secret.py | 20 + ...me_transcription_session_create_request.py | 119 +----- ...nscription_session_create_request_param.py | 118 +----- ...e_transcription_session_create_response.py | 41 ++ ...ption_session_input_audio_transcription.py | 36 ++ ...me_transcription_session_turn_detection.py | 32 ++ .../types/realtime/realtime_truncation.py | 20 +- .../realtime/realtime_truncation_param.py | 20 +- .../realtime_truncation_retention_ratio.py | 18 + ...altime_truncation_retention_ratio_param.py | 18 + .../types/realtime/response_create_event.py | 124 +----- .../realtime/response_create_event_param.py | 121 +----- .../types/realtime/session_created_event.py | 14 +- .../types/realtime/session_update_event.py | 23 +- .../realtime/session_update_event_param.py | 22 +- .../types/realtime/session_updated_event.py | 14 +- .../realtime/transcription_session_created.py | 99 +---- .../realtime/transcription_session_update.py | 86 +++- .../transcription_session_update_param.py | 85 +++- .../transcription_session_updated_event.py | 99 +---- .../realtime/test_client_secrets.py | 38 +- 89 files changed, 2603 insertions(+), 1896 deletions(-) create mode 100644 src/openai/types/realtime/audio_transcription.py create mode 100644 src/openai/types/realtime/audio_transcription_param.py create mode 100644 src/openai/types/realtime/models.py create mode 100644 src/openai/types/realtime/models_param.py create mode 100644 src/openai/types/realtime/noise_reduction_type.py create mode 100644 src/openai/types/realtime/realtime_audio_config_input.py create mode 100644 src/openai/types/realtime/realtime_audio_config_input_param.py create mode 100644 src/openai/types/realtime/realtime_audio_config_output.py create mode 100644 src/openai/types/realtime/realtime_audio_config_output_param.py create mode 100644 src/openai/types/realtime/realtime_audio_formats.py create mode 100644 src/openai/types/realtime/realtime_audio_formats_param.py create mode 100644 src/openai/types/realtime/realtime_audio_input_turn_detection.py create mode 100644 src/openai/types/realtime/realtime_audio_input_turn_detection_param.py delete mode 100644 src/openai/types/realtime/realtime_client_secret_config.py delete mode 100644 src/openai/types/realtime/realtime_client_secret_config_param.py create mode 100644 src/openai/types/realtime/realtime_response_create_audio_output.py create mode 100644 src/openai/types/realtime/realtime_response_create_audio_output_param.py create mode 100644 src/openai/types/realtime/realtime_response_create_mcp_tool.py create mode 100644 src/openai/types/realtime/realtime_response_create_mcp_tool_param.py create mode 100644 src/openai/types/realtime/realtime_response_create_params.py create mode 100644 src/openai/types/realtime/realtime_response_create_params_param.py delete mode 100644 src/openai/types/realtime/realtime_session.py create mode 100644 src/openai/types/realtime/realtime_session_client_secret.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_audio.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_audio_input.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_audio_input_param.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_audio_param.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_client_secret.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_create_response.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py create mode 100644 src/openai/types/realtime/realtime_transcription_session_turn_detection.py create mode 100644 src/openai/types/realtime/realtime_truncation_retention_ratio.py create mode 100644 src/openai/types/realtime/realtime_truncation_retention_ratio_param.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f2761d4022..12cec28d56 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.106.1" + ".": "1.107.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index c41be6ee57..36a3c7f587 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-51afd6abbcb18c3086f62993f9379c18443b9e516cbc0548ddfb932e835657f8.yml -openapi_spec_hash: dae6afeaefa15cb8700c7a870531e06f -config_hash: b854932c0ea24b400bdd64e4376936bd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7807ec6037efcee1af7decbfd3974a42b761fb6c6a71b4050fe43484d7fcbac4.yml +openapi_spec_hash: da6851e3891ad2659a50ed6a736fd32a +config_hash: 74d955cdc2377213f5268ea309090f6c diff --git a/CHANGELOG.md b/CHANGELOG.md index c0ad7d1490..76d5dcb2dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.107.0 (2025-09-08) + +Full Changelog: [v1.106.1...v1.107.0](https://github.com/openai/openai-python/compare/v1.106.1...v1.107.0) + +### Features + +* **api:** ship the RealtimeGA API shape ([dc319d8](https://github.com/openai/openai-python/commit/dc319d8bbb3a20108399c1d15f98e63bdd84eb5c)) + + +### Chores + +* **internal:** codegen related update ([b79b7ca](https://github.com/openai/openai-python/commit/b79b7ca3a72009a036db0a344b500f616ca0443f)) + ## 1.106.1 (2025-09-04) Full Changelog: [v1.106.0...v1.106.1](https://github.com/openai/openai-python/compare/v1.106.0...v1.106.1) diff --git a/api.md b/api.md index a8a95bd23e..7c947fffe1 100644 --- a/api.md +++ b/api.md @@ -863,6 +863,7 @@ Types: ```python from openai.types.realtime import ( + AudioTranscription, ConversationCreatedEvent, ConversationItem, ConversationItemAdded, @@ -891,11 +892,16 @@ from openai.types.realtime import ( McpListToolsCompleted, McpListToolsFailed, McpListToolsInProgress, + Models, + NoiseReductionType, OutputAudioBufferClearEvent, RateLimitsUpdatedEvent, RealtimeAudioConfig, + RealtimeAudioConfigInput, + RealtimeAudioConfigOutput, + RealtimeAudioFormats, + RealtimeAudioInputTurnDetection, RealtimeClientEvent, - RealtimeClientSecretConfig, RealtimeConversationItemAssistantMessage, RealtimeConversationItemFunctionCall, RealtimeConversationItemFunctionCallOutput, @@ -911,6 +917,9 @@ from openai.types.realtime import ( RealtimeMcpToolExecutionError, RealtimeMcphttpError, RealtimeResponse, + RealtimeResponseCreateAudioOutput, + RealtimeResponseCreateMcpTool, + RealtimeResponseCreateParams, RealtimeResponseStatus, RealtimeResponseUsage, RealtimeResponseUsageInputTokenDetails, @@ -922,8 +931,12 @@ from openai.types.realtime import ( RealtimeToolsConfig, RealtimeToolsConfigUnion, RealtimeTracingConfig, + RealtimeTranscriptionSessionAudio, + RealtimeTranscriptionSessionAudioInput, + RealtimeTranscriptionSessionAudioInputTurnDetection, RealtimeTranscriptionSessionCreateRequest, RealtimeTruncation, + RealtimeTruncationRetentionRatio, ResponseAudioDeltaEvent, ResponseAudioDoneEvent, ResponseAudioTranscriptDeltaEvent, @@ -959,7 +972,15 @@ from openai.types.realtime import ( Types: ```python -from openai.types.realtime import RealtimeSessionCreateResponse, ClientSecretCreateResponse +from openai.types.realtime import ( + RealtimeSessionClientSecret, + RealtimeSessionCreateResponse, + RealtimeTranscriptionSessionClientSecret, + RealtimeTranscriptionSessionCreateResponse, + RealtimeTranscriptionSessionInputAudioTranscription, + RealtimeTranscriptionSessionTurnDetection, + ClientSecretCreateResponse, +) ``` Methods: diff --git a/pyproject.toml b/pyproject.toml index 82aa72b045..5c3985cc7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.106.1" +version = "1.107.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/requirements-dev.lock b/requirements-dev.lock index 669378387d..7d690683e9 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -70,7 +70,7 @@ filelock==3.12.4 frozenlist==1.7.0 # via aiohttp # via aiosignal -griffe==1.13.0 +griffe==1.14.0 h11==0.16.0 # via httpcore httpcore==1.0.9 diff --git a/src/openai/_version.py b/src/openai/_version.py index 33c16fef6a..06826fc4de 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.106.1" # x-release-please-version +__version__ = "1.107.0" # x-release-please-version diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py index ba0f9ee538..a79460746d 100644 --- a/src/openai/resources/realtime/client_secrets.py +++ b/src/openai/resources/realtime/client_secrets.py @@ -50,11 +50,13 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ClientSecretCreateResponse: """ - Create a Realtime session and client secret for either realtime or - transcription. + Create a Realtime client secret with an associated session configuration. Args: - expires_after: Configuration for the ephemeral token expiration. + expires_after: Configuration for the client secret expiration. Expiration refers to the time + after which a client secret will no longer be valid for creating sessions. The + session itself may continue after that time once started. A secret can be used + to create multiple sessions until it expires. session: Session configuration to use for the client secret. Choose either a realtime session or a transcription session. @@ -116,11 +118,13 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ClientSecretCreateResponse: """ - Create a Realtime session and client secret for either realtime or - transcription. + Create a Realtime client secret with an associated session configuration. Args: - expires_after: Configuration for the ephemeral token expiration. + expires_after: Configuration for the client secret expiration. Expiration refers to the time + after which a client secret will no longer be valid for creating sessions. The + session itself may continue after that time once started. A secret can be used + to create multiple sessions until it expires. session: Session configuration to use for the client secret. Choose either a realtime session or a transcription session. diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 2f5adf6548..81e6dc54f5 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -32,16 +32,13 @@ ClientSecretsWithStreamingResponse, AsyncClientSecretsWithStreamingResponse, ) -from ...types.realtime import response_create_event_param +from ...types.realtime import session_update_event_param, transcription_session_update_param from ...types.websocket_connection_options import WebsocketConnectionOptions from ...types.realtime.realtime_client_event import RealtimeClientEvent from ...types.realtime.realtime_server_event import RealtimeServerEvent from ...types.realtime.conversation_item_param import ConversationItemParam from ...types.realtime.realtime_client_event_param import RealtimeClientEventParam -from ...types.realtime.realtime_session_create_request_param import RealtimeSessionCreateRequestParam -from ...types.realtime.realtime_transcription_session_create_request_param import ( - RealtimeTranscriptionSessionCreateRequestParam, -) +from ...types.realtime.realtime_response_create_params_param import RealtimeResponseCreateParamsParam if TYPE_CHECKING: from websockets.sync.client import ClientConnection as WebsocketConnection @@ -564,18 +561,18 @@ def __init__(self, connection: RealtimeConnection) -> None: class RealtimeSessionResource(BaseRealtimeConnectionResource): - def update(self, *, session: RealtimeSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN) -> None: + def update(self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN) -> None: """ - Send this event to update the session’s default configuration. - The client may send this event at any time to update any field, - except for `voice`. However, note that once a session has been - initialized with a particular `model`, it can’t be changed to - another model using `session.update`. + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other + audio outputs yet. When the server receives a `session.update`, it will respond with a `session.updated` event showing the full, effective configuration. - Only the fields that are present are updated. To clear a field like - `instructions`, pass an empty string. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. """ self._connection.send( cast( @@ -590,7 +587,7 @@ def create( self, *, event_id: str | NotGiven = NOT_GIVEN, - response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + response: RealtimeResponseCreateParamsParam | NotGiven = NOT_GIVEN, ) -> None: """ This event instructs the server to create a Response, which means triggering @@ -599,15 +596,25 @@ def create( A Response will include at least one Item, and may have two, in which case the second will be a function call. These Items will be appended to the - conversation history. + conversation history by default. The server will respond with a `response.created` event, events for Items and content created, and finally a `response.done` event to indicate the Response is complete. The `response.create` event includes inference configuration like - `instructions`, and `temperature`. These fields will override the Session's + `instructions` and `tools`. If these are set, they will override the Session's configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. """ self._connection.send( cast( @@ -621,7 +628,9 @@ def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | Not The server will respond with a `response.done` event with a status of `response.status=cancelled`. If - there is no response to cancel, the server will respond with an error. + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. """ self._connection.send( cast( @@ -644,16 +653,9 @@ def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: """ - Send this event to commit the user input audio buffer, which will create a - new user message item in the conversation. This event will produce an error - if the input audio buffer is empty. When in Server VAD mode, the client does - not need to send this event, the server will commit the audio buffer - automatically. + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. - Committing the input audio buffer will trigger input audio transcription - (if enabled in session configuration), but it will not create a response - from the model. The server will respond with an `input_audio_buffer.committed` - event. + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. """ self._connection.send( cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) @@ -663,14 +665,17 @@ def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: """Send this event to append audio bytes to the input audio buffer. The audio - buffer is temporary storage you can write to and later commit. In Server VAD - mode, the audio buffer is used to detect speech and the server will decide + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide when to commit. When Server VAD is disabled, you must commit the audio buffer - manually. + manually. Input audio noise reduction operates on writes to the audio buffer. The client may choose how much audio to place in each event up to a maximum of 15 MiB, for example streaming smaller chunks from the client may allow the - VAD to be more responsive. Unlike made other client events, the server will + VAD to be more responsive. Unlike most other client events, the server will not send a confirmation response to this event. """ self._connection.send( @@ -797,7 +802,7 @@ def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: class RealtimeTranscriptionSessionResource(BaseRealtimeConnectionResource): def update( - self, *, session: RealtimeTranscriptionSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN + self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN ) -> None: """Send this event to update a transcription session.""" self._connection.send( @@ -814,18 +819,20 @@ def __init__(self, connection: AsyncRealtimeConnection) -> None: class AsyncRealtimeSessionResource(BaseAsyncRealtimeConnectionResource): - async def update(self, *, session: RealtimeSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def update( + self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN + ) -> None: """ - Send this event to update the session’s default configuration. - The client may send this event at any time to update any field, - except for `voice`. However, note that once a session has been - initialized with a particular `model`, it can’t be changed to - another model using `session.update`. + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other + audio outputs yet. When the server receives a `session.update`, it will respond with a `session.updated` event showing the full, effective configuration. - Only the fields that are present are updated. To clear a field like - `instructions`, pass an empty string. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. """ await self._connection.send( cast( @@ -840,7 +847,7 @@ async def create( self, *, event_id: str | NotGiven = NOT_GIVEN, - response: response_create_event_param.Response | NotGiven = NOT_GIVEN, + response: RealtimeResponseCreateParamsParam | NotGiven = NOT_GIVEN, ) -> None: """ This event instructs the server to create a Response, which means triggering @@ -849,15 +856,25 @@ async def create( A Response will include at least one Item, and may have two, in which case the second will be a function call. These Items will be appended to the - conversation history. + conversation history by default. The server will respond with a `response.created` event, events for Items and content created, and finally a `response.done` event to indicate the Response is complete. The `response.create` event includes inference configuration like - `instructions`, and `temperature`. These fields will override the Session's + `instructions` and `tools`. If these are set, they will override the Session's configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. """ await self._connection.send( cast( @@ -871,7 +888,9 @@ async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str The server will respond with a `response.done` event with a status of `response.status=cancelled`. If - there is no response to cancel, the server will respond with an error. + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. """ await self._connection.send( cast( @@ -894,16 +913,9 @@ async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: """ - Send this event to commit the user input audio buffer, which will create a - new user message item in the conversation. This event will produce an error - if the input audio buffer is empty. When in Server VAD mode, the client does - not need to send this event, the server will commit the audio buffer - automatically. + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. - Committing the input audio buffer will trigger input audio transcription - (if enabled in session configuration), but it will not create a response - from the model. The server will respond with an `input_audio_buffer.committed` - event. + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. """ await self._connection.send( cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) @@ -913,14 +925,17 @@ async def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> N """Send this event to append audio bytes to the input audio buffer. The audio - buffer is temporary storage you can write to and later commit. In Server VAD - mode, the audio buffer is used to detect speech and the server will decide + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide when to commit. When Server VAD is disabled, you must commit the audio buffer - manually. + manually. Input audio noise reduction operates on writes to the audio buffer. The client may choose how much audio to place in each event up to a maximum of 15 MiB, for example streaming smaller chunks from the client may allow the - VAD to be more responsive. Unlike made other client events, the server will + VAD to be more responsive. Unlike most other client events, the server will not send a confirmation response to this event. """ await self._connection.send( @@ -1047,7 +1062,7 @@ async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: class AsyncRealtimeTranscriptionSessionResource(BaseAsyncRealtimeConnectionResource): async def update( - self, *, session: RealtimeTranscriptionSessionCreateRequestParam, event_id: str | NotGiven = NOT_GIVEN + self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN ) -> None: """Send this event to update a transcription session.""" await self._connection.send( diff --git a/src/openai/types/realtime/__init__.py b/src/openai/types/realtime/__init__.py index b05f620619..6873ba6a2a 100644 --- a/src/openai/types/realtime/__init__.py +++ b/src/openai/types/realtime/__init__.py @@ -2,13 +2,16 @@ from __future__ import annotations +from .models import Models as Models +from .models_param import ModelsParam as ModelsParam from .realtime_error import RealtimeError as RealtimeError -from .realtime_session import RealtimeSession as RealtimeSession from .conversation_item import ConversationItem as ConversationItem from .realtime_response import RealtimeResponse as RealtimeResponse +from .audio_transcription import AudioTranscription as AudioTranscription from .log_prob_properties import LogProbProperties as LogProbProperties from .realtime_truncation import RealtimeTruncation as RealtimeTruncation from .response_done_event import ResponseDoneEvent as ResponseDoneEvent +from .noise_reduction_type import NoiseReductionType as NoiseReductionType from .realtime_error_event import RealtimeErrorEvent as RealtimeErrorEvent from .session_update_event import SessionUpdateEvent as SessionUpdateEvent from .mcp_list_tools_failed import McpListToolsFailed as McpListToolsFailed @@ -21,6 +24,7 @@ from .session_created_event import SessionCreatedEvent as SessionCreatedEvent from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent from .conversation_item_done import ConversationItemDone as ConversationItemDone +from .realtime_audio_formats import RealtimeAudioFormats as RealtimeAudioFormats from .realtime_mcp_tool_call import RealtimeMcpToolCall as RealtimeMcpToolCall from .realtime_mcphttp_error import RealtimeMcphttpError as RealtimeMcphttpError from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent @@ -34,6 +38,7 @@ from .realtime_response_status import RealtimeResponseStatus as RealtimeResponseStatus from .response_mcp_call_failed import ResponseMcpCallFailed as ResponseMcpCallFailed from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent +from .audio_transcription_param import AudioTranscriptionParam as AudioTranscriptionParam from .rate_limits_updated_event import RateLimitsUpdatedEvent as RateLimitsUpdatedEvent from .realtime_truncation_param import RealtimeTruncationParam as RealtimeTruncationParam from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent @@ -43,6 +48,7 @@ from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent from .session_update_event_param import SessionUpdateEventParam as SessionUpdateEventParam from .client_secret_create_params import ClientSecretCreateParams as ClientSecretCreateParams +from .realtime_audio_config_input import RealtimeAudioConfigInput as RealtimeAudioConfigInput from .realtime_audio_config_param import RealtimeAudioConfigParam as RealtimeAudioConfigParam from .realtime_client_event_param import RealtimeClientEventParam as RealtimeClientEventParam from .realtime_mcp_protocol_error import RealtimeMcpProtocolError as RealtimeMcpProtocolError @@ -52,11 +58,12 @@ from .response_cancel_event_param import ResponseCancelEventParam as ResponseCancelEventParam from .response_create_event_param import ResponseCreateEventParam as ResponseCreateEventParam from .response_mcp_call_completed import ResponseMcpCallCompleted as ResponseMcpCallCompleted +from .realtime_audio_config_output import RealtimeAudioConfigOutput as RealtimeAudioConfigOutput +from .realtime_audio_formats_param import RealtimeAudioFormatsParam as RealtimeAudioFormatsParam from .realtime_mcp_tool_call_param import RealtimeMcpToolCallParam as RealtimeMcpToolCallParam from .realtime_mcphttp_error_param import RealtimeMcphttpErrorParam as RealtimeMcphttpErrorParam from .transcription_session_update import TranscriptionSessionUpdate as TranscriptionSessionUpdate from .client_secret_create_response import ClientSecretCreateResponse as ClientSecretCreateResponse -from .realtime_client_secret_config import RealtimeClientSecretConfig as RealtimeClientSecretConfig from .realtime_mcp_approval_request import RealtimeMcpApprovalRequest as RealtimeMcpApprovalRequest from .realtime_mcp_list_tools_param import RealtimeMcpListToolsParam as RealtimeMcpListToolsParam from .realtime_tracing_config_param import RealtimeTracingConfigParam as RealtimeTracingConfigParam @@ -66,11 +73,13 @@ from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent from .realtime_mcp_approval_response import RealtimeMcpApprovalResponse as RealtimeMcpApprovalResponse +from .realtime_session_client_secret import RealtimeSessionClientSecret as RealtimeSessionClientSecret from .conversation_item_created_event import ConversationItemCreatedEvent as ConversationItemCreatedEvent from .conversation_item_deleted_event import ConversationItemDeletedEvent as ConversationItemDeletedEvent from .input_audio_buffer_append_event import InputAudioBufferAppendEvent as InputAudioBufferAppendEvent from .input_audio_buffer_commit_event import InputAudioBufferCommitEvent as InputAudioBufferCommitEvent from .output_audio_buffer_clear_event import OutputAudioBufferClearEvent as OutputAudioBufferClearEvent +from .realtime_response_create_params import RealtimeResponseCreateParams as RealtimeResponseCreateParams from .realtime_session_create_request import RealtimeSessionCreateRequest as RealtimeSessionCreateRequest from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent from .conversation_item_retrieve_event import ConversationItemRetrieveEvent as ConversationItemRetrieveEvent @@ -81,26 +90,37 @@ from .response_mcp_call_arguments_done import ResponseMcpCallArgumentsDone as ResponseMcpCallArgumentsDone from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent from .conversation_item_truncated_event import ConversationItemTruncatedEvent as ConversationItemTruncatedEvent +from .realtime_audio_config_input_param import RealtimeAudioConfigInputParam as RealtimeAudioConfigInputParam from .realtime_mcp_protocol_error_param import RealtimeMcpProtocolErrorParam as RealtimeMcpProtocolErrorParam from .realtime_mcp_tool_execution_error import RealtimeMcpToolExecutionError as RealtimeMcpToolExecutionError +from .realtime_response_create_mcp_tool import RealtimeResponseCreateMcpTool as RealtimeResponseCreateMcpTool from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam as RealtimeToolChoiceConfigParam from .realtime_tools_config_union_param import RealtimeToolsConfigUnionParam as RealtimeToolsConfigUnionParam from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent from .response_mcp_call_arguments_delta import ResponseMcpCallArgumentsDelta as ResponseMcpCallArgumentsDelta from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent as InputAudioBufferCommittedEvent +from .realtime_audio_config_output_param import RealtimeAudioConfigOutputParam as RealtimeAudioConfigOutputParam from .transcription_session_update_param import TranscriptionSessionUpdateParam as TranscriptionSessionUpdateParam -from .realtime_client_secret_config_param import RealtimeClientSecretConfigParam as RealtimeClientSecretConfigParam +from .realtime_audio_input_turn_detection import RealtimeAudioInputTurnDetection as RealtimeAudioInputTurnDetection from .realtime_mcp_approval_request_param import RealtimeMcpApprovalRequestParam as RealtimeMcpApprovalRequestParam +from .realtime_truncation_retention_ratio import RealtimeTruncationRetentionRatio as RealtimeTruncationRetentionRatio from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent as TranscriptionSessionUpdatedEvent from .conversation_item_create_event_param import ConversationItemCreateEventParam as ConversationItemCreateEventParam from .conversation_item_delete_event_param import ConversationItemDeleteEventParam as ConversationItemDeleteEventParam from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam as InputAudioBufferClearEventParam from .input_audio_buffer_timeout_triggered import InputAudioBufferTimeoutTriggered as InputAudioBufferTimeoutTriggered from .realtime_mcp_approval_response_param import RealtimeMcpApprovalResponseParam as RealtimeMcpApprovalResponseParam +from .realtime_transcription_session_audio import RealtimeTranscriptionSessionAudio as RealtimeTranscriptionSessionAudio from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent from .input_audio_buffer_append_event_param import InputAudioBufferAppendEventParam as InputAudioBufferAppendEventParam from .input_audio_buffer_commit_event_param import InputAudioBufferCommitEventParam as InputAudioBufferCommitEventParam from .output_audio_buffer_clear_event_param import OutputAudioBufferClearEventParam as OutputAudioBufferClearEventParam +from .realtime_response_create_audio_output import ( + RealtimeResponseCreateAudioOutput as RealtimeResponseCreateAudioOutput, +) +from .realtime_response_create_params_param import ( + RealtimeResponseCreateParamsParam as RealtimeResponseCreateParamsParam, +) from .realtime_session_create_request_param import ( RealtimeSessionCreateRequestParam as RealtimeSessionCreateRequestParam, ) @@ -125,12 +145,30 @@ from .realtime_mcp_tool_execution_error_param import ( RealtimeMcpToolExecutionErrorParam as RealtimeMcpToolExecutionErrorParam, ) +from .realtime_response_create_mcp_tool_param import ( + RealtimeResponseCreateMcpToolParam as RealtimeResponseCreateMcpToolParam, +) from .realtime_conversation_item_function_call import ( RealtimeConversationItemFunctionCall as RealtimeConversationItemFunctionCall, ) +from .realtime_audio_input_turn_detection_param import ( + RealtimeAudioInputTurnDetectionParam as RealtimeAudioInputTurnDetectionParam, +) from .realtime_conversation_item_system_message import ( RealtimeConversationItemSystemMessage as RealtimeConversationItemSystemMessage, ) +from .realtime_truncation_retention_ratio_param import ( + RealtimeTruncationRetentionRatioParam as RealtimeTruncationRetentionRatioParam, +) +from .realtime_transcription_session_audio_input import ( + RealtimeTranscriptionSessionAudioInput as RealtimeTranscriptionSessionAudioInput, +) +from .realtime_transcription_session_audio_param import ( + RealtimeTranscriptionSessionAudioParam as RealtimeTranscriptionSessionAudioParam, +) +from .realtime_response_create_audio_output_param import ( + RealtimeResponseCreateAudioOutputParam as RealtimeResponseCreateAudioOutputParam, +) from .realtime_response_usage_input_token_details import ( RealtimeResponseUsageInputTokenDetails as RealtimeResponseUsageInputTokenDetails, ) @@ -143,6 +181,9 @@ from .realtime_response_usage_output_token_details import ( RealtimeResponseUsageOutputTokenDetails as RealtimeResponseUsageOutputTokenDetails, ) +from .realtime_transcription_session_client_secret import ( + RealtimeTranscriptionSessionClientSecret as RealtimeTranscriptionSessionClientSecret, +) from .response_function_call_arguments_delta_event import ( ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, ) @@ -152,15 +193,24 @@ from .realtime_transcription_session_create_request import ( RealtimeTranscriptionSessionCreateRequest as RealtimeTranscriptionSessionCreateRequest, ) +from .realtime_transcription_session_turn_detection import ( + RealtimeTranscriptionSessionTurnDetection as RealtimeTranscriptionSessionTurnDetection, +) from .realtime_conversation_item_function_call_param import ( RealtimeConversationItemFunctionCallParam as RealtimeConversationItemFunctionCallParam, ) +from .realtime_transcription_session_create_response import ( + RealtimeTranscriptionSessionCreateResponse as RealtimeTranscriptionSessionCreateResponse, +) from .realtime_conversation_item_function_call_output import ( RealtimeConversationItemFunctionCallOutput as RealtimeConversationItemFunctionCallOutput, ) from .realtime_conversation_item_system_message_param import ( RealtimeConversationItemSystemMessageParam as RealtimeConversationItemSystemMessageParam, ) +from .realtime_transcription_session_audio_input_param import ( + RealtimeTranscriptionSessionAudioInputParam as RealtimeTranscriptionSessionAudioInputParam, +) from .realtime_conversation_item_assistant_message_param import ( RealtimeConversationItemAssistantMessageParam as RealtimeConversationItemAssistantMessageParam, ) @@ -179,6 +229,15 @@ from .conversation_item_input_audio_transcription_failed_event import ( ConversationItemInputAudioTranscriptionFailedEvent as ConversationItemInputAudioTranscriptionFailedEvent, ) +from .realtime_transcription_session_input_audio_transcription import ( + RealtimeTranscriptionSessionInputAudioTranscription as RealtimeTranscriptionSessionInputAudioTranscription, +) +from .realtime_transcription_session_audio_input_turn_detection import ( + RealtimeTranscriptionSessionAudioInputTurnDetection as RealtimeTranscriptionSessionAudioInputTurnDetection, +) from .conversation_item_input_audio_transcription_completed_event import ( ConversationItemInputAudioTranscriptionCompletedEvent as ConversationItemInputAudioTranscriptionCompletedEvent, ) +from .realtime_transcription_session_audio_input_turn_detection_param import ( + RealtimeTranscriptionSessionAudioInputTurnDetectionParam as RealtimeTranscriptionSessionAudioInputTurnDetectionParam, +) diff --git a/src/openai/types/realtime/audio_transcription.py b/src/openai/types/realtime/audio_transcription.py new file mode 100644 index 0000000000..cf662b3aa2 --- /dev/null +++ b/src/openai/types/realtime/audio_transcription.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AudioTranscription"] + + +class AudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["whisper-1", "gpt-4o-transcribe-latest", "gpt-4o-mini-transcribe", "gpt-4o-transcribe"]] = ( + None + ) + """The model to use for transcription. + + Current options are `whisper-1`, `gpt-4o-transcribe-latest`, + `gpt-4o-mini-transcribe`, and `gpt-4o-transcribe`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ diff --git a/src/openai/types/realtime/audio_transcription_param.py b/src/openai/types/realtime/audio_transcription_param.py new file mode 100644 index 0000000000..fb09f105b8 --- /dev/null +++ b/src/openai/types/realtime/audio_transcription_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["AudioTranscriptionParam"] + + +class AudioTranscriptionParam(TypedDict, total=False): + language: str + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Literal["whisper-1", "gpt-4o-transcribe-latest", "gpt-4o-mini-transcribe", "gpt-4o-transcribe"] + """The model to use for transcription. + + Current options are `whisper-1`, `gpt-4o-transcribe-latest`, + `gpt-4o-mini-transcribe`, and `gpt-4o-transcribe`. + """ + + prompt: str + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ diff --git a/src/openai/types/realtime/client_secret_create_params.py b/src/openai/types/realtime/client_secret_create_params.py index 696176e5a8..5f0b0d796f 100644 --- a/src/openai/types/realtime/client_secret_create_params.py +++ b/src/openai/types/realtime/client_secret_create_params.py @@ -13,7 +13,12 @@ class ClientSecretCreateParams(TypedDict, total=False): expires_after: ExpiresAfter - """Configuration for the ephemeral token expiration.""" + """Configuration for the client secret expiration. + + Expiration refers to the time after which a client secret will no longer be + valid for creating sessions. The session itself may continue after that time + once started. A secret can be used to create multiple sessions until it expires. + """ session: Session """Session configuration to use for the client secret. @@ -24,15 +29,17 @@ class ClientSecretCreateParams(TypedDict, total=False): class ExpiresAfter(TypedDict, total=False): anchor: Literal["created_at"] - """The anchor point for the ephemeral token expiration. - - Only `created_at` is currently supported. + """ + The anchor point for the client secret expiration, meaning that `seconds` will + be added to the `created_at` time of the client secret to produce an expiration + timestamp. Only `created_at` is currently supported. """ seconds: int """The number of seconds from the anchor point to the expiration. - Select a value between `10` and `7200`. + Select a value between `10` and `7200` (2 hours). This default to 600 seconds + (10 minutes) if not specified. """ diff --git a/src/openai/types/realtime/client_secret_create_response.py b/src/openai/types/realtime/client_secret_create_response.py index ea8b9f9ca1..8d61be3ab7 100644 --- a/src/openai/types/realtime/client_secret_create_response.py +++ b/src/openai/types/realtime/client_secret_create_response.py @@ -1,102 +1,15 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional -from typing_extensions import Literal, TypeAlias +from typing import Union +from typing_extensions import TypeAlias from ..._models import BaseModel from .realtime_session_create_response import RealtimeSessionCreateResponse +from .realtime_transcription_session_create_response import RealtimeTranscriptionSessionCreateResponse -__all__ = [ - "ClientSecretCreateResponse", - "Session", - "SessionRealtimeTranscriptionSessionCreateResponse", - "SessionRealtimeTranscriptionSessionCreateResponseAudio", - "SessionRealtimeTranscriptionSessionCreateResponseAudioInput", - "SessionRealtimeTranscriptionSessionCreateResponseAudioInputNoiseReduction", - "SessionRealtimeTranscriptionSessionCreateResponseAudioInputTranscription", - "SessionRealtimeTranscriptionSessionCreateResponseAudioInputTurnDetection", -] +__all__ = ["ClientSecretCreateResponse", "Session"] - -class SessionRealtimeTranscriptionSessionCreateResponseAudioInputNoiseReduction(BaseModel): - type: Optional[Literal["near_field", "far_field"]] = None - - -class SessionRealtimeTranscriptionSessionCreateResponseAudioInputTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None - """The model to use for transcription. - - Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. - """ - - prompt: Optional[str] = None - """An optional text to guide the model's style or continue a previous audio - segment. - - The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. - """ - - -class SessionRealtimeTranscriptionSessionCreateResponseAudioInputTurnDetection(BaseModel): - prefix_padding_ms: Optional[int] = None - - silence_duration_ms: Optional[int] = None - - threshold: Optional[float] = None - - type: Optional[str] = None - """Type of turn detection, only `server_vad` is currently supported.""" - - -class SessionRealtimeTranscriptionSessionCreateResponseAudioInput(BaseModel): - format: Optional[str] = None - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" - - noise_reduction: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInputNoiseReduction] = None - """Configuration for input audio noise reduction.""" - - transcription: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInputTranscription] = None - """Configuration of the transcription model.""" - - turn_detection: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInputTurnDetection] = None - """Configuration for turn detection.""" - - -class SessionRealtimeTranscriptionSessionCreateResponseAudio(BaseModel): - input: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudioInput] = None - - -class SessionRealtimeTranscriptionSessionCreateResponse(BaseModel): - id: Optional[str] = None - """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" - - audio: Optional[SessionRealtimeTranscriptionSessionCreateResponseAudio] = None - """Configuration for input audio for the session.""" - - expires_at: Optional[int] = None - """Expiration timestamp for the session, in seconds since epoch.""" - - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None - """Additional fields to include in server outputs. - - - `item.input_audio_transcription.logprobs`: Include logprobs for input audio - transcription. - """ - - object: Optional[str] = None - """The object type. Always `realtime.transcription_session`.""" - - -Session: TypeAlias = Union[RealtimeSessionCreateResponse, SessionRealtimeTranscriptionSessionCreateResponse] +Session: TypeAlias = Union[RealtimeSessionCreateResponse, RealtimeTranscriptionSessionCreateResponse] class ClientSecretCreateResponse(BaseModel): diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py index eda3f3bab6..09b20aa184 100644 --- a/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -59,7 +59,7 @@ class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): """The unique ID of the server event.""" item_id: str - """The ID of the user message item containing the audio.""" + """The ID of the item containing the audio that is being transcribed.""" transcript: str """The transcribed text.""" @@ -70,7 +70,10 @@ class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): """ usage: Usage - """Usage statistics for the transcription.""" + """ + Usage statistics for the transcription, this is billed according to the ASR + model's pricing rather than the realtime model's pricing. + """ logprobs: Optional[List[LogProbProperties]] = None """The log probabilities of the transcription.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py index 4e9528ccb0..f49e6f636f 100644 --- a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py @@ -14,7 +14,7 @@ class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel): """The unique ID of the server event.""" item_id: str - """The ID of the item.""" + """The ID of the item containing the audio that is being transcribed.""" type: Literal["conversation.item.input_audio_transcription.delta"] """The event type, must be `conversation.item.input_audio_transcription.delta`.""" @@ -26,4 +26,11 @@ class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel): """The text delta.""" logprobs: Optional[List[LogProbProperties]] = None - """The log probabilities of the transcription.""" + """The log probabilities of the transcription. + + These can be enabled by configurating the session with + `"include": ["item.input_audio_transcription.logprobs"]`. Each entry in the + array corresponds a log probability of which token would be selected for this + chunk of transcription. This can help to identify if it was possible there were + multiple valid options for a given chunk of transcription. + """ diff --git a/src/openai/types/realtime/conversation_item_truncate_event.py b/src/openai/types/realtime/conversation_item_truncate_event.py index 63b591bfdb..d6c6779cc8 100644 --- a/src/openai/types/realtime/conversation_item_truncate_event.py +++ b/src/openai/types/realtime/conversation_item_truncate_event.py @@ -17,7 +17,7 @@ class ConversationItemTruncateEvent(BaseModel): """ content_index: int - """The index of the content part to truncate. Set this to 0.""" + """The index of the content part to truncate. Set this to `0`.""" item_id: str """The ID of the assistant message item to truncate. diff --git a/src/openai/types/realtime/conversation_item_truncate_event_param.py b/src/openai/types/realtime/conversation_item_truncate_event_param.py index d3ad1e1e25..f5ab13a419 100644 --- a/src/openai/types/realtime/conversation_item_truncate_event_param.py +++ b/src/openai/types/realtime/conversation_item_truncate_event_param.py @@ -16,7 +16,7 @@ class ConversationItemTruncateEventParam(TypedDict, total=False): """ content_index: Required[int] - """The index of the content part to truncate. Set this to 0.""" + """The index of the content part to truncate. Set this to `0`.""" item_id: Required[str] """The ID of the assistant message item to truncate. diff --git a/src/openai/types/realtime/models.py b/src/openai/types/realtime/models.py new file mode 100644 index 0000000000..d4827538a3 --- /dev/null +++ b/src/openai/types/realtime/models.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["Models"] + + +class Models(BaseModel): + description: Optional[str] = None + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: Optional[str] = None + """The name of the function.""" + + parameters: Optional[object] = None + """Parameters of the function in JSON Schema.""" + + type: Optional[Literal["function"]] = None + """The type of the tool, i.e. `function`.""" diff --git a/src/openai/types/realtime/models_param.py b/src/openai/types/realtime/models_param.py new file mode 100644 index 0000000000..1db2d7e464 --- /dev/null +++ b/src/openai/types/realtime/models_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ModelsParam"] + + +class ModelsParam(TypedDict, total=False): + description: str + """ + The description of the function, including guidance on when and how to call it, + and guidance about what to tell the user when calling (if anything). + """ + + name: str + """The name of the function.""" + + parameters: object + """Parameters of the function in JSON Schema.""" + + type: Literal["function"] + """The type of the tool, i.e. `function`.""" diff --git a/src/openai/types/realtime/noise_reduction_type.py b/src/openai/types/realtime/noise_reduction_type.py new file mode 100644 index 0000000000..f4338991bb --- /dev/null +++ b/src/openai/types/realtime/noise_reduction_type.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["NoiseReductionType"] + +NoiseReductionType: TypeAlias = Literal["near_field", "far_field"] diff --git a/src/openai/types/realtime/realtime_audio_config.py b/src/openai/types/realtime/realtime_audio_config.py index 7463c70038..72d7cc59cc 100644 --- a/src/openai/types/realtime/realtime_audio_config.py +++ b/src/openai/types/realtime/realtime_audio_config.py @@ -1,184 +1,15 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union, Optional -from typing_extensions import Literal +from typing import Optional from ..._models import BaseModel +from .realtime_audio_config_input import RealtimeAudioConfigInput +from .realtime_audio_config_output import RealtimeAudioConfigOutput -__all__ = ["RealtimeAudioConfig", "Input", "InputNoiseReduction", "InputTranscription", "InputTurnDetection", "Output"] - - -class InputNoiseReduction(BaseModel): - type: Optional[Literal["near_field", "far_field"]] = None - """Type of noise reduction. - - `near_field` is for close-talking microphones such as headphones, `far_field` is - for far-field microphones such as laptop or conference room microphones. - """ - - -class InputTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Optional[ - Literal[ - "whisper-1", - "gpt-4o-transcribe-latest", - "gpt-4o-mini-transcribe", - "gpt-4o-transcribe", - "gpt-4o-transcribe-diarize", - ] - ] = None - """The model to use for transcription. - - Current options are `whisper-1`, `gpt-4o-transcribe-latest`, - `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, and `gpt-4o-transcribe-diarize`. - """ - - prompt: Optional[str] = None - """ - An optional text to guide the model's style or continue a previous audio - segment. For `whisper-1`, the - [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". - """ - - -class InputTurnDetection(BaseModel): - create_response: Optional[bool] = None - """ - Whether or not to automatically generate a response when a VAD stop event - occurs. - """ - - eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None - """Used only for `semantic_vad` mode. - - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. - """ - - idle_timeout_ms: Optional[int] = None - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. - """ - - interrupt_response: Optional[bool] = None - """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. - """ - - prefix_padding_ms: Optional[int] = None - """Used only for `server_vad` mode. - - Amount of audio to include before the VAD detected speech (in milliseconds). - Defaults to 300ms. - """ - - silence_duration_ms: Optional[int] = None - """Used only for `server_vad` mode. - - Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. - With shorter values the model will respond more quickly, but may jump in on - short pauses from the user. - """ - - threshold: Optional[float] = None - """Used only for `server_vad` mode. - - Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher - threshold will require louder audio to activate the model, and thus might - perform better in noisy environments. - """ - - type: Optional[Literal["server_vad", "semantic_vad"]] = None - """Type of turn detection.""" - - -class Input(BaseModel): - format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of input audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must - be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian - byte order. - """ - - noise_reduction: Optional[InputNoiseReduction] = None - """Configuration for input audio noise reduction. - - This can be set to `null` to turn off. Noise reduction filters audio added to - the input audio buffer before it is sent to VAD and the model. Filtering the - audio can improve VAD and turn detection accuracy (reducing false positives) and - model performance by improving perception of the input audio. - """ - - transcription: Optional[InputTranscription] = None - """ - Configuration for input audio transcription, defaults to off and can be set to - `null` to turn off once on. Input audio transcription is not native to the - model, since the model consumes audio directly. Transcription runs - asynchronously through - [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as guidance of input audio content rather than precisely - what the model heard. The client can optionally set the language and prompt for - transcription, these offer additional guidance to the transcription service. - """ - - turn_detection: Optional[InputTurnDetection] = None - """Configuration for turn detection, ether Server VAD or Semantic VAD. - - This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjunction - with VAD) to semantically estimate whether the user has finished speaking, then - dynamically sets a timeout based on this probability. For example, if user audio - trails off with "uhhm", the model will score a low probability of turn end and - wait longer for the user to continue speaking. This can be useful for more - natural conversations, but may have a higher latency. - """ - - -class Output(BaseModel): - format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of output audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is - sampled at a rate of 24kHz. - """ - - speed: Optional[float] = None - """The speed of the model's spoken response. - - 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. - This value can only be changed in between model turns, not while a response is - in progress. - """ - - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None - ] = None - """The voice the model uses to respond. - - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. - """ +__all__ = ["RealtimeAudioConfig"] class RealtimeAudioConfig(BaseModel): - input: Optional[Input] = None + input: Optional[RealtimeAudioConfigInput] = None - output: Optional[Output] = None + output: Optional[RealtimeAudioConfigOutput] = None diff --git a/src/openai/types/realtime/realtime_audio_config_input.py b/src/openai/types/realtime/realtime_audio_config_input.py new file mode 100644 index 0000000000..fd96e2a52d --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_config_input.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .audio_transcription import AudioTranscription +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_audio_input_turn_detection import RealtimeAudioInputTurnDetection + +__all__ = ["RealtimeAudioConfigInput", "NoiseReduction"] + + +class NoiseReduction(BaseModel): + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeAudioConfigInput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the input audio.""" + + noise_reduction: Optional[NoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: Optional[AudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: Optional[RealtimeAudioInputTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ diff --git a/src/openai/types/realtime/realtime_audio_config_input_param.py b/src/openai/types/realtime/realtime_audio_config_input_param.py new file mode 100644 index 0000000000..1dfb439006 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_config_input_param.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .noise_reduction_type import NoiseReductionType +from .audio_transcription_param import AudioTranscriptionParam +from .realtime_audio_formats_param import RealtimeAudioFormatsParam +from .realtime_audio_input_turn_detection_param import RealtimeAudioInputTurnDetectionParam + +__all__ = ["RealtimeAudioConfigInputParam", "NoiseReduction"] + + +class NoiseReduction(TypedDict, total=False): + type: NoiseReductionType + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeAudioConfigInputParam(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The format of the input audio.""" + + noise_reduction: NoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: AudioTranscriptionParam + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: RealtimeAudioInputTurnDetectionParam + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ diff --git a/src/openai/types/realtime/realtime_audio_config_output.py b/src/openai/types/realtime/realtime_audio_config_output.py new file mode 100644 index 0000000000..a8af237c1d --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_config_output.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_audio_formats import RealtimeAudioFormats + +__all__ = ["RealtimeAudioConfigOutput"] + + +class RealtimeAudioConfigOutput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" + + speed: Optional[float] = None + """ + The speed of the model's spoken response as a multiple of the original speed. + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + + This parameter is a post-processing adjustment to the audio after it is + generated, it's also possible to prompt the model to speak faster or slower. + """ + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ diff --git a/src/openai/types/realtime/realtime_audio_config_output_param.py b/src/openai/types/realtime/realtime_audio_config_output_param.py new file mode 100644 index 0000000000..8e887d3464 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_config_output_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +from .realtime_audio_formats_param import RealtimeAudioFormatsParam + +__all__ = ["RealtimeAudioConfigOutputParam"] + + +class RealtimeAudioConfigOutputParam(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The format of the output audio.""" + + speed: float + """ + The speed of the model's spoken response as a multiple of the original speed. + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + + This parameter is a post-processing adjustment to the audio after it is + generated, it's also possible to prompt the model to speak faster or slower. + """ + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ diff --git a/src/openai/types/realtime/realtime_audio_config_param.py b/src/openai/types/realtime/realtime_audio_config_param.py index 9f2e12e910..2c41de35ae 100644 --- a/src/openai/types/realtime/realtime_audio_config_param.py +++ b/src/openai/types/realtime/realtime_audio_config_param.py @@ -2,186 +2,15 @@ from __future__ import annotations -from typing import Union, Optional -from typing_extensions import Literal, TypedDict +from typing_extensions import TypedDict -__all__ = [ - "RealtimeAudioConfigParam", - "Input", - "InputNoiseReduction", - "InputTranscription", - "InputTurnDetection", - "Output", -] +from .realtime_audio_config_input_param import RealtimeAudioConfigInputParam +from .realtime_audio_config_output_param import RealtimeAudioConfigOutputParam - -class InputNoiseReduction(TypedDict, total=False): - type: Literal["near_field", "far_field"] - """Type of noise reduction. - - `near_field` is for close-talking microphones such as headphones, `far_field` is - for far-field microphones such as laptop or conference room microphones. - """ - - -class InputTranscription(TypedDict, total=False): - language: str - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Literal[ - "whisper-1", - "gpt-4o-transcribe-latest", - "gpt-4o-mini-transcribe", - "gpt-4o-transcribe", - "gpt-4o-transcribe-diarize", - ] - """The model to use for transcription. - - Current options are `whisper-1`, `gpt-4o-transcribe-latest`, - `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, and `gpt-4o-transcribe-diarize`. - """ - - prompt: str - """ - An optional text to guide the model's style or continue a previous audio - segment. For `whisper-1`, the - [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". - """ - - -class InputTurnDetection(TypedDict, total=False): - create_response: bool - """ - Whether or not to automatically generate a response when a VAD stop event - occurs. - """ - - eagerness: Literal["low", "medium", "high", "auto"] - """Used only for `semantic_vad` mode. - - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. - """ - - idle_timeout_ms: Optional[int] - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. - """ - - interrupt_response: bool - """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. - """ - - prefix_padding_ms: int - """Used only for `server_vad` mode. - - Amount of audio to include before the VAD detected speech (in milliseconds). - Defaults to 300ms. - """ - - silence_duration_ms: int - """Used only for `server_vad` mode. - - Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. - With shorter values the model will respond more quickly, but may jump in on - short pauses from the user. - """ - - threshold: float - """Used only for `server_vad` mode. - - Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher - threshold will require louder audio to activate the model, and thus might - perform better in noisy environments. - """ - - type: Literal["server_vad", "semantic_vad"] - """Type of turn detection.""" - - -class Input(TypedDict, total=False): - format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of input audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must - be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian - byte order. - """ - - noise_reduction: InputNoiseReduction - """Configuration for input audio noise reduction. - - This can be set to `null` to turn off. Noise reduction filters audio added to - the input audio buffer before it is sent to VAD and the model. Filtering the - audio can improve VAD and turn detection accuracy (reducing false positives) and - model performance by improving perception of the input audio. - """ - - transcription: InputTranscription - """ - Configuration for input audio transcription, defaults to off and can be set to - `null` to turn off once on. Input audio transcription is not native to the - model, since the model consumes audio directly. Transcription runs - asynchronously through - [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as guidance of input audio content rather than precisely - what the model heard. The client can optionally set the language and prompt for - transcription, these offer additional guidance to the transcription service. - """ - - turn_detection: InputTurnDetection - """Configuration for turn detection, ether Server VAD or Semantic VAD. - - This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjunction - with VAD) to semantically estimate whether the user has finished speaking, then - dynamically sets a timeout based on this probability. For example, if user audio - trails off with "uhhm", the model will score a low probability of turn end and - wait longer for the user to continue speaking. This can be useful for more - natural conversations, but may have a higher latency. - """ - - -class Output(TypedDict, total=False): - format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of output audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is - sampled at a rate of 24kHz. - """ - - speed: float - """The speed of the model's spoken response. - - 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. - This value can only be changed in between model turns, not while a response is - in progress. - """ - - voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] - """The voice the model uses to respond. - - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. - """ +__all__ = ["RealtimeAudioConfigParam"] class RealtimeAudioConfigParam(TypedDict, total=False): - input: Input + input: RealtimeAudioConfigInputParam - output: Output + output: RealtimeAudioConfigOutputParam diff --git a/src/openai/types/realtime/realtime_audio_formats.py b/src/openai/types/realtime/realtime_audio_formats.py new file mode 100644 index 0000000000..10f91883b6 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_formats.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["RealtimeAudioFormats", "AudioPCM", "AudioPCMU", "AudioPCMA"] + + +class AudioPCM(BaseModel): + rate: Optional[Literal[24000]] = None + """The sample rate of the audio. Always `24000`.""" + + type: Optional[Literal["audio/pcm"]] = None + """The audio format. Always `audio/pcm`.""" + + +class AudioPCMU(BaseModel): + type: Optional[Literal["audio/pcmu"]] = None + """The audio format. Always `audio/pcmu`.""" + + +class AudioPCMA(BaseModel): + type: Optional[Literal["audio/pcma"]] = None + """The audio format. Always `audio/pcma`.""" + + +RealtimeAudioFormats: TypeAlias = Annotated[Union[AudioPCM, AudioPCMU, AudioPCMA], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/realtime/realtime_audio_formats_param.py b/src/openai/types/realtime/realtime_audio_formats_param.py new file mode 100644 index 0000000000..cf58577f38 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_formats_param.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias, TypedDict + +__all__ = ["RealtimeAudioFormatsParam", "AudioPCM", "AudioPCMU", "AudioPCMA"] + + +class AudioPCM(TypedDict, total=False): + rate: Literal[24000] + """The sample rate of the audio. Always `24000`.""" + + type: Literal["audio/pcm"] + """The audio format. Always `audio/pcm`.""" + + +class AudioPCMU(TypedDict, total=False): + type: Literal["audio/pcmu"] + """The audio format. Always `audio/pcmu`.""" + + +class AudioPCMA(TypedDict, total=False): + type: Literal["audio/pcma"] + """The audio format. Always `audio/pcma`.""" + + +RealtimeAudioFormatsParam: TypeAlias = Union[AudioPCM, AudioPCMU, AudioPCMA] diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_audio_input_turn_detection.py new file mode 100644 index 0000000000..ea9423f6a1 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeAudioInputTurnDetection"] + + +class RealtimeAudioInputTurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + idle_timeout_ms: Optional[int] = None + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py new file mode 100644 index 0000000000..ec398f52e6 --- /dev/null +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["RealtimeAudioInputTurnDetectionParam"] + + +class RealtimeAudioInputTurnDetectionParam(TypedDict, total=False): + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + idle_timeout_ms: Optional[int] + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" diff --git a/src/openai/types/realtime/realtime_client_secret_config.py b/src/openai/types/realtime/realtime_client_secret_config.py deleted file mode 100644 index 29f8f57081..0000000000 --- a/src/openai/types/realtime/realtime_client_secret_config.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["RealtimeClientSecretConfig", "ExpiresAfter"] - - -class ExpiresAfter(BaseModel): - anchor: Literal["created_at"] - """The anchor point for the ephemeral token expiration. - - Only `created_at` is currently supported. - """ - - seconds: Optional[int] = None - """The number of seconds from the anchor point to the expiration. - - Select a value between `10` and `7200`. - """ - - -class RealtimeClientSecretConfig(BaseModel): - expires_after: Optional[ExpiresAfter] = None - """Configuration for the ephemeral token expiration.""" diff --git a/src/openai/types/realtime/realtime_client_secret_config_param.py b/src/openai/types/realtime/realtime_client_secret_config_param.py deleted file mode 100644 index 30a80134ee..0000000000 --- a/src/openai/types/realtime/realtime_client_secret_config_param.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["RealtimeClientSecretConfigParam", "ExpiresAfter"] - - -class ExpiresAfter(TypedDict, total=False): - anchor: Required[Literal["created_at"]] - """The anchor point for the ephemeral token expiration. - - Only `created_at` is currently supported. - """ - - seconds: int - """The number of seconds from the anchor point to the expiration. - - Select a value between `10` and `7200`. - """ - - -class RealtimeClientSecretConfigParam(TypedDict, total=False): - expires_after: ExpiresAfter - """Configuration for the ephemeral token expiration.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py index d0f37745ea..6b0f86ee32 100644 --- a/src/openai/types/realtime/realtime_conversation_item_assistant_message.py +++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py @@ -9,11 +9,27 @@ class Content(BaseModel): + audio: Optional[str] = None + """ + Base64-encoded audio bytes, these will be parsed as the format specified in the + session output audio type configuration. This defaults to PCM 16-bit 24kHz mono + if not specified. + """ + text: Optional[str] = None """The text content.""" - type: Optional[Literal["text"]] = None - """The content type. Always `text` for assistant messages.""" + transcript: Optional[str] = None + """ + The transcript of the audio content, this will always be present if the output + type is `audio`. + """ + + type: Optional[Literal["output_text", "output_audio"]] = None + """ + The content type, `output_text` or `output_audio` depending on the session + `output_modalities` configuration. + """ class RealtimeConversationItemAssistantMessage(BaseModel): @@ -27,10 +43,16 @@ class RealtimeConversationItemAssistantMessage(BaseModel): """The type of the item. Always `message`.""" id: Optional[str] = None - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Optional[Literal["realtime.item"]] = None - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Optional[Literal["completed", "incomplete", "in_progress"]] = None """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py index cfbd9cd2cf..93699afba2 100644 --- a/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py @@ -9,11 +9,27 @@ class Content(TypedDict, total=False): + audio: str + """ + Base64-encoded audio bytes, these will be parsed as the format specified in the + session output audio type configuration. This defaults to PCM 16-bit 24kHz mono + if not specified. + """ + text: str """The text content.""" - type: Literal["text"] - """The content type. Always `text` for assistant messages.""" + transcript: str + """ + The transcript of the audio content, this will always be present if the output + type is `audio`. + """ + + type: Literal["output_text", "output_audio"] + """ + The content type, `output_text` or `output_audio` depending on the session + `output_modalities` configuration. + """ class RealtimeConversationItemAssistantMessageParam(TypedDict, total=False): @@ -27,10 +43,16 @@ class RealtimeConversationItemAssistantMessageParam(TypedDict, total=False): """The type of the item. Always `message`.""" id: str - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Literal["realtime.item"] - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Literal["completed", "incomplete", "in_progress"] """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call.py b/src/openai/types/realtime/realtime_conversation_item_function_call.py index ce1c6d4cb2..279a2fcdc5 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call.py @@ -10,7 +10,11 @@ class RealtimeConversationItemFunctionCall(BaseModel): arguments: str - """The arguments of the function call.""" + """The arguments of the function call. + + This is a JSON-encoded string representing the arguments passed to the function, + for example `{"arg1": "value1", "arg2": 42}`. + """ name: str """The name of the function being called.""" @@ -19,13 +23,19 @@ class RealtimeConversationItemFunctionCall(BaseModel): """The type of the item. Always `function_call`.""" id: Optional[str] = None - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ call_id: Optional[str] = None """The ID of the function call.""" object: Optional[Literal["realtime.item"]] = None - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Optional[Literal["completed", "incomplete", "in_progress"]] = None """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py index cea840fdba..4b6b15d0ad 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call_output.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py @@ -13,16 +13,25 @@ class RealtimeConversationItemFunctionCallOutput(BaseModel): """The ID of the function call this output is for.""" output: str - """The output of the function call.""" + """ + The output of the function call, this is free text and can contain any + information or simply be empty. + """ type: Literal["function_call_output"] """The type of the item. Always `function_call_output`.""" id: Optional[str] = None - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Optional[Literal["realtime.item"]] = None - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Optional[Literal["completed", "incomplete", "in_progress"]] = None """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py index a66c587fb6..56d62da563 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py @@ -12,16 +12,25 @@ class RealtimeConversationItemFunctionCallOutputParam(TypedDict, total=False): """The ID of the function call this output is for.""" output: Required[str] - """The output of the function call.""" + """ + The output of the function call, this is free text and can contain any + information or simply be empty. + """ type: Required[Literal["function_call_output"]] """The type of the item. Always `function_call_output`.""" id: str - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Literal["realtime.item"] - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Literal["completed", "incomplete", "in_progress"] """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py index a4d6fb83ab..36a16a27b3 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py @@ -9,7 +9,11 @@ class RealtimeConversationItemFunctionCallParam(TypedDict, total=False): arguments: Required[str] - """The arguments of the function call.""" + """The arguments of the function call. + + This is a JSON-encoded string representing the arguments passed to the function, + for example `{"arg1": "value1", "arg2": 42}`. + """ name: Required[str] """The name of the function being called.""" @@ -18,13 +22,19 @@ class RealtimeConversationItemFunctionCallParam(TypedDict, total=False): """The type of the item. Always `function_call`.""" id: str - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ call_id: str """The ID of the function call.""" object: Literal["realtime.item"] - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Literal["completed", "incomplete", "in_progress"] """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message.py b/src/openai/types/realtime/realtime_conversation_item_system_message.py index abc67f6c5f..7dac5c9fe2 100644 --- a/src/openai/types/realtime/realtime_conversation_item_system_message.py +++ b/src/openai/types/realtime/realtime_conversation_item_system_message.py @@ -27,10 +27,16 @@ class RealtimeConversationItemSystemMessage(BaseModel): """The type of the item. Always `message`.""" id: Optional[str] = None - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Optional[Literal["realtime.item"]] = None - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Optional[Literal["completed", "incomplete", "in_progress"]] = None """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message_param.py b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py index 2a1c442738..a2790fcf67 100644 --- a/src/openai/types/realtime/realtime_conversation_item_system_message_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py @@ -27,10 +27,16 @@ class RealtimeConversationItemSystemMessageParam(TypedDict, total=False): """The type of the item. Always `message`.""" id: str - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Literal["realtime.item"] - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Literal["completed", "incomplete", "in_progress"] """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message.py b/src/openai/types/realtime/realtime_conversation_item_user_message.py index 48a6c6ec0a..30d9bb10e3 100644 --- a/src/openai/types/realtime/realtime_conversation_item_user_message.py +++ b/src/openai/types/realtime/realtime_conversation_item_user_message.py @@ -10,16 +10,37 @@ class Content(BaseModel): audio: Optional[str] = None - """Base64-encoded audio bytes (for `input_audio`).""" + """ + Base64-encoded audio bytes (for `input_audio`), these will be parsed as the + format specified in the session input audio type configuration. This defaults to + PCM 16-bit 24kHz mono if not specified. + """ + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the image (for `input_image`). + + `auto` will default to `high`. + """ + + image_url: Optional[str] = None + """Base64-encoded image bytes (for `input_image`) as a data URI. + + For example `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`. Supported + formats are PNG and JPEG. + """ text: Optional[str] = None """The text content (for `input_text`).""" transcript: Optional[str] = None - """Transcript of the audio (for `input_audio`).""" + """Transcript of the audio (for `input_audio`). - type: Optional[Literal["input_text", "input_audio"]] = None - """The content type (`input_text` or `input_audio`).""" + This is not sent to the model, but will be attached to the message item for + reference. + """ + + type: Optional[Literal["input_text", "input_audio", "input_image"]] = None + """The content type (`input_text`, `input_audio`, or `input_image`).""" class RealtimeConversationItemUserMessage(BaseModel): @@ -33,10 +54,16 @@ class RealtimeConversationItemUserMessage(BaseModel): """The type of the item. Always `message`.""" id: Optional[str] = None - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Optional[Literal["realtime.item"]] = None - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Optional[Literal["completed", "incomplete", "in_progress"]] = None """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message_param.py b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py index cff64a66bf..7d3b9bc137 100644 --- a/src/openai/types/realtime/realtime_conversation_item_user_message_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py @@ -10,16 +10,37 @@ class Content(TypedDict, total=False): audio: str - """Base64-encoded audio bytes (for `input_audio`).""" + """ + Base64-encoded audio bytes (for `input_audio`), these will be parsed as the + format specified in the session input audio type configuration. This defaults to + PCM 16-bit 24kHz mono if not specified. + """ + + detail: Literal["auto", "low", "high"] + """The detail level of the image (for `input_image`). + + `auto` will default to `high`. + """ + + image_url: str + """Base64-encoded image bytes (for `input_image`) as a data URI. + + For example `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`. Supported + formats are PNG and JPEG. + """ text: str """The text content (for `input_text`).""" transcript: str - """Transcript of the audio (for `input_audio`).""" + """Transcript of the audio (for `input_audio`). - type: Literal["input_text", "input_audio"] - """The content type (`input_text` or `input_audio`).""" + This is not sent to the model, but will be attached to the message item for + reference. + """ + + type: Literal["input_text", "input_audio", "input_image"] + """The content type (`input_text`, `input_audio`, or `input_image`).""" class RealtimeConversationItemUserMessageParam(TypedDict, total=False): @@ -33,10 +54,16 @@ class RealtimeConversationItemUserMessageParam(TypedDict, total=False): """The type of the item. Always `message`.""" id: str - """The unique ID of the item.""" + """The unique ID of the item. + + This may be provided by the client or generated by the server. + """ object: Literal["realtime.item"] - """Identifier for the API object being returned - always `realtime.item`.""" + """Identifier for the API object being returned - always `realtime.item`. + + Optional when creating a new item. + """ status: Literal["completed", "incomplete", "in_progress"] """The status of the item. Has no effect on the conversation.""" diff --git a/src/openai/types/realtime/realtime_response.py b/src/openai/types/realtime/realtime_response.py index 54f5999b81..92d75491c0 100644 --- a/src/openai/types/realtime/realtime_response.py +++ b/src/openai/types/realtime/realtime_response.py @@ -6,15 +6,39 @@ from ..._models import BaseModel from ..shared.metadata import Metadata from .conversation_item import ConversationItem +from .realtime_audio_formats import RealtimeAudioFormats from .realtime_response_usage import RealtimeResponseUsage from .realtime_response_status import RealtimeResponseStatus -__all__ = ["RealtimeResponse"] +__all__ = ["RealtimeResponse", "Audio", "AudioOutput"] + + +class AudioOutput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ + + +class Audio(BaseModel): + output: Optional[AudioOutput] = None class RealtimeResponse(BaseModel): id: Optional[str] = None - """The unique ID of the response.""" + """The unique ID of the response, will look like `resp_1234`.""" + + audio: Optional[Audio] = None + """Configuration for audio output.""" conversation_id: Optional[str] = None """ @@ -23,8 +47,7 @@ class RealtimeResponse(BaseModel): the default conversation and the value of `conversation_id` will be an id like `conv_1234`. If `none`, the response will not be added to any conversation and the value of `conversation_id` will be `null`. If responses are being triggered - by server VAD, the response will be added to the default conversation, thus the - `conversation_id` will be an id like `conv_1234`. + automatically by VAD the response will be added to the default conversation """ max_output_tokens: Union[int, Literal["inf"], None] = None @@ -43,22 +66,19 @@ class RealtimeResponse(BaseModel): a maximum length of 512 characters. """ - modalities: Optional[List[Literal["text", "audio"]]] = None - """The set of modalities the model used to respond. - - If there are multiple modalities, the model will pick one, for example if - `modalities` is `["text", "audio"]`, the model could be responding in either - text or audio. - """ - object: Optional[Literal["realtime.response"]] = None """The object type, must be `realtime.response`.""" output: Optional[List[ConversationItem]] = None """The list of output items generated by the response.""" - output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """ + The set of modalities the model used to respond, currently the only possible + values are `[\"audio\"]`, `[\"text\"]`. Audio output always include a text + transcript. Setting the output to mode `text` will disable audio output from the + model. + """ status: Optional[Literal["completed", "cancelled", "failed", "incomplete", "in_progress"]] = None """ @@ -69,9 +89,6 @@ class RealtimeResponse(BaseModel): status_details: Optional[RealtimeResponseStatus] = None """Additional details about the status.""" - temperature: Optional[float] = None - """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" - usage: Optional[RealtimeResponseUsage] = None """Usage statistics for the Response, this will correspond to billing. @@ -79,11 +96,3 @@ class RealtimeResponse(BaseModel): to the Conversation, thus output from previous turns (text and audio tokens) will become the input for later turns. """ - - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None - ] = None - """ - The voice the model used to respond. Current voice options are `alloy`, `ash`, - `ballad`, `coral`, `echo`, `sage`, `shimmer`, and `verse`. - """ diff --git a/src/openai/types/realtime/realtime_response_create_audio_output.py b/src/openai/types/realtime/realtime_response_create_audio_output.py new file mode 100644 index 0000000000..48a5d67e20 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_create_audio_output.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_audio_formats import RealtimeAudioFormats + +__all__ = ["RealtimeResponseCreateAudioOutput", "Output"] + + +class Output(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" + + voice: Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None + ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ + + +class RealtimeResponseCreateAudioOutput(BaseModel): + output: Optional[Output] = None diff --git a/src/openai/types/realtime/realtime_response_create_audio_output_param.py b/src/openai/types/realtime/realtime_response_create_audio_output_param.py new file mode 100644 index 0000000000..9aa6d28835 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_create_audio_output_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypedDict + +from .realtime_audio_formats_param import RealtimeAudioFormatsParam + +__all__ = ["RealtimeResponseCreateAudioOutputParam", "Output"] + + +class Output(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The format of the output audio.""" + + voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ + + +class RealtimeResponseCreateAudioOutputParam(TypedDict, total=False): + output: Output diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py new file mode 100644 index 0000000000..119b4a455d --- /dev/null +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py @@ -0,0 +1,135 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = [ + "RealtimeResponseCreateMcpTool", + "AllowedTools", + "AllowedToolsMcpToolFilter", + "RequireApproval", + "RequireApprovalMcpToolApprovalFilter", + "RequireApprovalMcpToolApprovalFilterAlways", + "RequireApprovalMcpToolApprovalFilterNever", +] + + +class AllowedToolsMcpToolFilter(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +AllowedTools: TypeAlias = Union[List[str], AllowedToolsMcpToolFilter, None] + + +class RequireApprovalMcpToolApprovalFilterAlways(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilterNever(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilter(BaseModel): + always: Optional[RequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[RequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +RequireApproval: TypeAlias = Union[RequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] + + +class RealtimeResponseCreateMcpTool(BaseModel): + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_tools: Optional[AllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[RequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url` or `connector_id` must be provided. + """ diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py new file mode 100644 index 0000000000..3b9cf047c1 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py @@ -0,0 +1,135 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "RealtimeResponseCreateMcpToolParam", + "AllowedTools", + "AllowedToolsMcpToolFilter", + "RequireApproval", + "RequireApprovalMcpToolApprovalFilter", + "RequireApprovalMcpToolApprovalFilterAlways", + "RequireApprovalMcpToolApprovalFilterNever", +] + + +class AllowedToolsMcpToolFilter(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +AllowedTools: TypeAlias = Union[SequenceNotStr[str], AllowedToolsMcpToolFilter] + + +class RequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class RequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + always: RequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: RequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +RequireApproval: TypeAlias = Union[RequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class RealtimeResponseCreateMcpToolParam(TypedDict, total=False): + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_tools: Optional[AllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[RequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url` or `connector_id` must be provided. + """ diff --git a/src/openai/types/realtime/realtime_response_create_params.py b/src/openai/types/realtime/realtime_response_create_params.py new file mode 100644 index 0000000000..3b5a8907a1 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_create_params.py @@ -0,0 +1,98 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from .models import Models +from ..._models import BaseModel +from ..shared.metadata import Metadata +from .conversation_item import ConversationItem +from ..responses.response_prompt import ResponsePrompt +from ..responses.tool_choice_mcp import ToolChoiceMcp +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.tool_choice_function import ToolChoiceFunction +from .realtime_response_create_mcp_tool import RealtimeResponseCreateMcpTool +from .realtime_response_create_audio_output import RealtimeResponseCreateAudioOutput + +__all__ = ["RealtimeResponseCreateParams", "ToolChoice", "Tool"] + +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] + +Tool: TypeAlias = Union[Models, RealtimeResponseCreateMcpTool] + + +class RealtimeResponseCreateParams(BaseModel): + audio: Optional[RealtimeResponseCreateAudioOutput] = None + """Configuration for audio input and output.""" + + conversation: Union[str, Literal["auto", "none"], None] = None + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Optional[List[ConversationItem]] = None + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items that previously + appeared in the session using their id. + """ + + instructions: Optional[str] = None + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. Note that the server sets default instructions which will be used if + this field is not set and are visible in the `session.created` event at the + start of the session. + """ + + max_output_tokens: Union[int, Literal["inf"], None] = None + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + output_modalities: Optional[List[Literal["text", "audio"]]] = None + """ + The set of modalities the model used to respond, currently the only possible + values are `[\"audio\"]`, `[\"text\"]`. Audio output always include a text + transcript. Setting the output to mode `text` will disable audio output from the + model. + """ + + prompt: Optional[ResponsePrompt] = None + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + tool_choice: Optional[ToolChoice] = None + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Optional[List[Tool]] = None + """Tools available to the model.""" diff --git a/src/openai/types/realtime/realtime_response_create_params_param.py b/src/openai/types/realtime/realtime_response_create_params_param.py new file mode 100644 index 0000000000..6800d36a31 --- /dev/null +++ b/src/openai/types/realtime/realtime_response_create_params_param.py @@ -0,0 +1,99 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, TypeAlias, TypedDict + +from .models_param import ModelsParam +from ..shared_params.metadata import Metadata +from .conversation_item_param import ConversationItemParam +from ..responses.tool_choice_options import ToolChoiceOptions +from ..responses.response_prompt_param import ResponsePromptParam +from ..responses.tool_choice_mcp_param import ToolChoiceMcpParam +from ..responses.tool_choice_function_param import ToolChoiceFunctionParam +from .realtime_response_create_mcp_tool_param import RealtimeResponseCreateMcpToolParam +from .realtime_response_create_audio_output_param import RealtimeResponseCreateAudioOutputParam + +__all__ = ["RealtimeResponseCreateParamsParam", "ToolChoice", "Tool"] + +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunctionParam, ToolChoiceMcpParam] + +Tool: TypeAlias = Union[ModelsParam, RealtimeResponseCreateMcpToolParam] + + +class RealtimeResponseCreateParamsParam(TypedDict, total=False): + audio: RealtimeResponseCreateAudioOutputParam + """Configuration for audio input and output.""" + + conversation: Union[str, Literal["auto", "none"]] + """Controls which conversation the response is added to. + + Currently supports `auto` and `none`, with `auto` as the default value. The + `auto` value means that the contents of the response will be added to the + default conversation. Set this to `none` to create an out-of-band response which + will not add items to default conversation. + """ + + input: Iterable[ConversationItemParam] + """Input items to include in the prompt for the model. + + Using this field creates a new context for this Response instead of using the + default conversation. An empty array `[]` will clear the context for this + Response. Note that this can include references to items that previously + appeared in the session using their id. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. Note that the server sets default instructions which will be used if + this field is not set and are visible in the `session.created` event at the + start of the session. + """ + + max_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + output_modalities: List[Literal["text", "audio"]] + """ + The set of modalities the model used to respond, currently the only possible + values are `[\"audio\"]`, `[\"text\"]`. Audio output always include a text + transcript. Setting the output to mode `text` will disable audio output from the + model. + """ + + prompt: Optional[ResponsePromptParam] + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + tool_choice: ToolChoice + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: Iterable[Tool] + """Tools available to the model.""" diff --git a/src/openai/types/realtime/realtime_response_usage.py b/src/openai/types/realtime/realtime_response_usage.py index dbce5f28c3..fb8893b346 100644 --- a/src/openai/types/realtime/realtime_response_usage.py +++ b/src/openai/types/realtime/realtime_response_usage.py @@ -11,7 +11,13 @@ class RealtimeResponseUsage(BaseModel): input_token_details: Optional[RealtimeResponseUsageInputTokenDetails] = None - """Details about the input tokens used in the Response.""" + """Details about the input tokens used in the Response. + + Cached tokens are tokens from previous turns in the conversation that are + included as context for the current response. Cached tokens here are counted as + a subset of input tokens, meaning input tokens will include cached and uncached + tokens. + """ input_tokens: Optional[int] = None """ diff --git a/src/openai/types/realtime/realtime_response_usage_input_token_details.py b/src/openai/types/realtime/realtime_response_usage_input_token_details.py index dfeead90ef..e14a74a84e 100644 --- a/src/openai/types/realtime/realtime_response_usage_input_token_details.py +++ b/src/openai/types/realtime/realtime_response_usage_input_token_details.py @@ -4,15 +4,32 @@ from ..._models import BaseModel -__all__ = ["RealtimeResponseUsageInputTokenDetails"] +__all__ = ["RealtimeResponseUsageInputTokenDetails", "CachedTokensDetails"] + + +class CachedTokensDetails(BaseModel): + audio_tokens: Optional[int] = None + """The number of cached audio tokens used as input for the Response.""" + + image_tokens: Optional[int] = None + """The number of cached image tokens used as input for the Response.""" + + text_tokens: Optional[int] = None + """The number of cached text tokens used as input for the Response.""" class RealtimeResponseUsageInputTokenDetails(BaseModel): audio_tokens: Optional[int] = None - """The number of audio tokens used in the Response.""" + """The number of audio tokens used as input for the Response.""" cached_tokens: Optional[int] = None - """The number of cached tokens used in the Response.""" + """The number of cached tokens used as input for the Response.""" + + cached_tokens_details: Optional[CachedTokensDetails] = None + """Details about the cached tokens used as input for the Response.""" + + image_tokens: Optional[int] = None + """The number of image tokens used as input for the Response.""" text_tokens: Optional[int] = None - """The number of text tokens used in the Response.""" + """The number of text tokens used as input for the Response.""" diff --git a/src/openai/types/realtime/realtime_session.py b/src/openai/types/realtime/realtime_session.py deleted file mode 100644 index fdb5e9419a..0000000000 --- a/src/openai/types/realtime/realtime_session.py +++ /dev/null @@ -1,307 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union, Optional -from typing_extensions import Literal, TypeAlias - -from ..._models import BaseModel -from ..responses.response_prompt import ResponsePrompt - -__all__ = [ - "RealtimeSession", - "InputAudioNoiseReduction", - "InputAudioTranscription", - "Tool", - "Tracing", - "TracingTracingConfiguration", - "TurnDetection", -] - - -class InputAudioNoiseReduction(BaseModel): - type: Optional[Literal["near_field", "far_field"]] = None - """Type of noise reduction. - - `near_field` is for close-talking microphones such as headphones, `far_field` is - for far-field microphones such as laptop or conference room microphones. - """ - - -class InputAudioTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Optional[str] = None - """ - The model to use for transcription, current options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1`. - """ - - prompt: Optional[str] = None - """ - An optional text to guide the model's style or continue a previous audio - segment. For `whisper-1`, the - [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". - """ - - -class Tool(BaseModel): - description: Optional[str] = None - """ - The description of the function, including guidance on when and how to call it, - and guidance about what to tell the user when calling (if anything). - """ - - name: Optional[str] = None - """The name of the function.""" - - parameters: Optional[object] = None - """Parameters of the function in JSON Schema.""" - - type: Optional[Literal["function"]] = None - """The type of the tool, i.e. `function`.""" - - -class TracingTracingConfiguration(BaseModel): - group_id: Optional[str] = None - """ - The group id to attach to this trace to enable filtering and grouping in the - traces dashboard. - """ - - metadata: Optional[object] = None - """ - The arbitrary metadata to attach to this trace to enable filtering in the traces - dashboard. - """ - - workflow_name: Optional[str] = None - """The name of the workflow to attach to this trace. - - This is used to name the trace in the traces dashboard. - """ - - -Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration, None] - - -class TurnDetection(BaseModel): - create_response: Optional[bool] = None - """ - Whether or not to automatically generate a response when a VAD stop event - occurs. - """ - - eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None - """Used only for `semantic_vad` mode. - - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. - """ - - idle_timeout_ms: Optional[int] = None - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. - """ - - interrupt_response: Optional[bool] = None - """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. - """ - - prefix_padding_ms: Optional[int] = None - """Used only for `server_vad` mode. - - Amount of audio to include before the VAD detected speech (in milliseconds). - Defaults to 300ms. - """ - - silence_duration_ms: Optional[int] = None - """Used only for `server_vad` mode. - - Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. - With shorter values the model will respond more quickly, but may jump in on - short pauses from the user. - """ - - threshold: Optional[float] = None - """Used only for `server_vad` mode. - - Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher - threshold will require louder audio to activate the model, and thus might - perform better in noisy environments. - """ - - type: Optional[Literal["server_vad", "semantic_vad"]] = None - """Type of turn detection.""" - - -class RealtimeSession(BaseModel): - id: Optional[str] = None - """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" - - expires_at: Optional[int] = None - """Expiration timestamp for the session, in seconds since epoch.""" - - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None - """Additional fields to include in server outputs. - - - `item.input_audio_transcription.logprobs`: Include logprobs for input audio - transcription. - """ - - input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of input audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must - be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian - byte order. - """ - - input_audio_noise_reduction: Optional[InputAudioNoiseReduction] = None - """Configuration for input audio noise reduction. - - This can be set to `null` to turn off. Noise reduction filters audio added to - the input audio buffer before it is sent to VAD and the model. Filtering the - audio can improve VAD and turn detection accuracy (reducing false positives) and - model performance by improving perception of the input audio. - """ - - input_audio_transcription: Optional[InputAudioTranscription] = None - """ - Configuration for input audio transcription, defaults to off and can be set to - `null` to turn off once on. Input audio transcription is not native to the - model, since the model consumes audio directly. Transcription runs - asynchronously through - [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as guidance of input audio content rather than precisely - what the model heard. The client can optionally set the language and prompt for - transcription, these offer additional guidance to the transcription service. - """ - - instructions: Optional[str] = None - """The default system instructions (i.e. - - system message) prepended to model calls. This field allows the client to guide - the model on desired responses. The model can be instructed on response content - and format, (e.g. "be extremely succinct", "act friendly", "here are examples of - good responses") and on audio behavior (e.g. "talk quickly", "inject emotion - into your voice", "laugh frequently"). The instructions are not guaranteed to be - followed by the model, but they provide guidance to the model on the desired - behavior. - - Note that the server sets default instructions which will be used if this field - is not set and are visible in the `session.created` event at the start of the - session. - """ - - max_response_output_tokens: Union[int, Literal["inf"], None] = None - """ - Maximum number of output tokens for a single assistant response, inclusive of - tool calls. Provide an integer between 1 and 4096 to limit output tokens, or - `inf` for the maximum available tokens for a given model. Defaults to `inf`. - """ - - modalities: Optional[List[Literal["text", "audio"]]] = None - """The set of modalities the model can respond with. - - To disable audio, set this to ["text"]. - """ - - model: Optional[ - Literal[ - "gpt-realtime", - "gpt-realtime-2025-08-28", - "gpt-4o-realtime-preview", - "gpt-4o-realtime-preview-2024-10-01", - "gpt-4o-realtime-preview-2024-12-17", - "gpt-4o-realtime-preview-2025-06-03", - "gpt-4o-mini-realtime-preview", - "gpt-4o-mini-realtime-preview-2024-12-17", - ] - ] = None - """The Realtime model used for this session.""" - - object: Optional[Literal["realtime.session"]] = None - """The object type. Always `realtime.session`.""" - - output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of output audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, output audio is - sampled at a rate of 24kHz. - """ - - prompt: Optional[ResponsePrompt] = None - """Reference to a prompt template and its variables. - - [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). - """ - - speed: Optional[float] = None - """The speed of the model's spoken response. - - 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. - This value can only be changed in between model turns, not while a response is - in progress. - """ - - temperature: Optional[float] = None - """Sampling temperature for the model, limited to [0.6, 1.2]. - - For audio models a temperature of 0.8 is highly recommended for best - performance. - """ - - tool_choice: Optional[str] = None - """How the model chooses tools. - - Options are `auto`, `none`, `required`, or specify a function. - """ - - tools: Optional[List[Tool]] = None - """Tools (functions) available to the model.""" - - tracing: Optional[Tracing] = None - """Configuration options for tracing. - - Set to null to disable tracing. Once tracing is enabled for a session, the - configuration cannot be modified. - - `auto` will create a trace for the session with default values for the workflow - name, group id, and metadata. - """ - - turn_detection: Optional[TurnDetection] = None - """Configuration for turn detection, ether Server VAD or Semantic VAD. - - This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. - Semantic VAD is more advanced and uses a turn detection model (in conjunction - with VAD) to semantically estimate whether the user has finished speaking, then - dynamically sets a timeout based on this probability. For example, if user audio - trails off with "uhhm", the model will score a low probability of turn end and - wait longer for the user to continue speaking. This can be useful for more - natural conversations, but may have a higher latency. - """ - - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None - ] = None - """The voice the model uses to respond. - - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, and `verse`. - """ diff --git a/src/openai/types/realtime/realtime_session_client_secret.py b/src/openai/types/realtime/realtime_session_client_secret.py new file mode 100644 index 0000000000..a4998802bb --- /dev/null +++ b/src/openai/types/realtime/realtime_session_client_secret.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["RealtimeSessionClientSecret"] + + +class RealtimeSessionClientSecret(BaseModel): + expires_at: int + """Timestamp for when the token expires. + + Currently, all tokens expire after one minute. + """ + + value: str + """ + Ephemeral key usable in client environments to authenticate connections to the + Realtime API. Use this in client-side environments rather than a standard API + token, which should only be used server-side. + """ diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 85205add50..578bc43821 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -10,43 +10,22 @@ from .realtime_tracing_config import RealtimeTracingConfig from ..responses.response_prompt import ResponsePrompt from .realtime_tool_choice_config import RealtimeToolChoiceConfig -from .realtime_client_secret_config import RealtimeClientSecretConfig __all__ = ["RealtimeSessionCreateRequest"] class RealtimeSessionCreateRequest(BaseModel): - model: Union[ - str, - Literal[ - "gpt-realtime", - "gpt-realtime-2025-08-28", - "gpt-4o-realtime", - "gpt-4o-mini-realtime", - "gpt-4o-realtime-preview", - "gpt-4o-realtime-preview-2024-10-01", - "gpt-4o-realtime-preview-2024-12-17", - "gpt-4o-realtime-preview-2025-06-03", - "gpt-4o-mini-realtime-preview", - "gpt-4o-mini-realtime-preview-2024-12-17", - ], - ] - """The Realtime model used for this session.""" - type: Literal["realtime"] """The type of session to create. Always `realtime` for the Realtime API.""" audio: Optional[RealtimeAudioConfig] = None """Configuration for input and output audio.""" - client_secret: Optional[RealtimeClientSecretConfig] = None - """Configuration options for the generated client secret.""" - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None """Additional fields to include in server outputs. - - `item.input_audio_transcription.logprobs`: Include logprobs for input audio - transcription. + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. """ instructions: Optional[str] = None @@ -72,10 +51,28 @@ class RealtimeSessionCreateRequest(BaseModel): `inf` for the maximum available tokens for a given model. Defaults to `inf`. """ + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + None, + ] = None + """The Realtime model used for this session.""" + output_modalities: Optional[List[Literal["text", "audio"]]] = None """The set of modalities the model can respond with. - To disable audio, set this to ["text"]. + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. """ prompt: Optional[ResponsePrompt] = None @@ -84,13 +81,6 @@ class RealtimeSessionCreateRequest(BaseModel): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ - temperature: Optional[float] = None - """Sampling temperature for the model, limited to [0.6, 1.2]. - - For audio models a temperature of 0.8 is highly recommended for best - performance. - """ - tool_choice: Optional[RealtimeToolChoiceConfig] = None """How the model chooses tools. @@ -101,10 +91,10 @@ class RealtimeSessionCreateRequest(BaseModel): """Tools available to the model.""" tracing: Optional[RealtimeTracingConfig] = None - """Configuration options for tracing. - - Set to null to disable tracing. Once tracing is enabled for a session, the - configuration cannot be modified. + """ + Realtime API can write session traces to the + [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once + tracing is enabled for a session, the configuration cannot be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. @@ -113,6 +103,5 @@ class RealtimeSessionCreateRequest(BaseModel): truncation: Optional[RealtimeTruncation] = None """ Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. When set to `retention_ratio`, the server retains a - fraction of the conversation tokens prior to the instructions. + The default is `auto`. """ diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index 8f962ca0e2..5f7819fa61 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -11,45 +11,22 @@ from .realtime_tracing_config_param import RealtimeTracingConfigParam from ..responses.response_prompt_param import ResponsePromptParam from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam -from .realtime_client_secret_config_param import RealtimeClientSecretConfigParam __all__ = ["RealtimeSessionCreateRequestParam"] class RealtimeSessionCreateRequestParam(TypedDict, total=False): - model: Required[ - Union[ - str, - Literal[ - "gpt-realtime", - "gpt-realtime-2025-08-28", - "gpt-4o-realtime", - "gpt-4o-mini-realtime", - "gpt-4o-realtime-preview", - "gpt-4o-realtime-preview-2024-10-01", - "gpt-4o-realtime-preview-2024-12-17", - "gpt-4o-realtime-preview-2025-06-03", - "gpt-4o-mini-realtime-preview", - "gpt-4o-mini-realtime-preview-2024-12-17", - ], - ] - ] - """The Realtime model used for this session.""" - type: Required[Literal["realtime"]] """The type of session to create. Always `realtime` for the Realtime API.""" audio: RealtimeAudioConfigParam """Configuration for input and output audio.""" - client_secret: RealtimeClientSecretConfigParam - """Configuration options for the generated client secret.""" - include: List[Literal["item.input_audio_transcription.logprobs"]] """Additional fields to include in server outputs. - - `item.input_audio_transcription.logprobs`: Include logprobs for input audio - transcription. + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. """ instructions: str @@ -75,10 +52,27 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): `inf` for the maximum available tokens for a given model. Defaults to `inf`. """ + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + ] + """The Realtime model used for this session.""" + output_modalities: List[Literal["text", "audio"]] """The set of modalities the model can respond with. - To disable audio, set this to ["text"]. + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. """ prompt: Optional[ResponsePromptParam] @@ -87,13 +81,6 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ - temperature: float - """Sampling temperature for the model, limited to [0.6, 1.2]. - - For audio models a temperature of 0.8 is highly recommended for best - performance. - """ - tool_choice: RealtimeToolChoiceConfigParam """How the model chooses tools. @@ -104,10 +91,10 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): """Tools available to the model.""" tracing: Optional[RealtimeTracingConfigParam] - """Configuration options for tracing. - - Set to null to disable tracing. Once tracing is enabled for a session, the - configuration cannot be modified. + """ + Realtime API can write session traces to the + [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once + tracing is enabled for a session, the configuration cannot be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. @@ -116,6 +103,5 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): truncation: RealtimeTruncationParam """ Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. When set to `retention_ratio`, the server retains a - fraction of the conversation tokens prior to the instructions. + The default is `auto`. """ diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 82fa426982..9c10b84588 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -1,74 +1,171 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, TypeAlias +from .models import Models from ..._models import BaseModel +from .audio_transcription import AudioTranscription +from .realtime_truncation import RealtimeTruncation +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats +from ..responses.response_prompt import ResponsePrompt +from ..responses.tool_choice_mcp import ToolChoiceMcp +from ..responses.tool_choice_options import ToolChoiceOptions +from .realtime_session_client_secret import RealtimeSessionClientSecret +from ..responses.tool_choice_function import ToolChoiceFunction __all__ = [ "RealtimeSessionCreateResponse", "Audio", "AudioInput", "AudioInputNoiseReduction", - "AudioInputTranscription", "AudioInputTurnDetection", "AudioOutput", + "ToolChoice", "Tool", + "ToolMcpTool", + "ToolMcpToolAllowedTools", + "ToolMcpToolAllowedToolsMcpToolFilter", + "ToolMcpToolRequireApproval", + "ToolMcpToolRequireApprovalMcpToolApprovalFilter", + "ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways", + "ToolMcpToolRequireApprovalMcpToolApprovalFilterNever", "Tracing", "TracingTracingConfiguration", - "TurnDetection", ] class AudioInputNoiseReduction(BaseModel): - type: Optional[Literal["near_field", "far_field"]] = None + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ -class AudioInputTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio.""" - model: Optional[str] = None - """The model to use for transcription.""" +class AudioInputTurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ - prompt: Optional[str] = None - """Optional text to guide the model's style or continue a previous audio segment.""" + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + idle_timeout_ms: Optional[int] = None + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ -class AudioInputTurnDetection(BaseModel): prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ threshold: Optional[float] = None + """Used only for `server_vad` mode. - type: Optional[str] = None - """Type of turn detection, only `server_vad` is currently supported.""" + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" class AudioInput(BaseModel): - format: Optional[str] = None - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + format: Optional[RealtimeAudioFormats] = None + """The format of the input audio.""" noise_reduction: Optional[AudioInputNoiseReduction] = None - """Configuration for input audio noise reduction.""" + """Configuration for input audio noise reduction. - transcription: Optional[AudioInputTranscription] = None - """Configuration for input audio transcription.""" + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: Optional[AudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ turn_detection: Optional[AudioInputTurnDetection] = None - """Configuration for turn detection.""" + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ class AudioOutput(BaseModel): - format: Optional[str] = None - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + format: Optional[RealtimeAudioFormats] = None + """The format of the output audio.""" speed: Optional[float] = None + """ + The speed of the model's spoken response as a multiple of the original speed. + 1.0 is the default speed. 0.25 is the minimum speed. 1.5 is the maximum speed. + This value can only be changed in between model turns, not while a response is + in progress. + + This parameter is a post-processing adjustment to the audio after it is + generated, it's also possible to prompt the model to speak faster or slower. + """ voice: Union[ str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None ] = None + """The voice the model uses to respond. + + Voice cannot be changed during the session once the model has responded with + audio at least once. Current voice options are `alloy`, `ash`, `ballad`, + `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + `marin` and `cedar` for best quality. + """ class Audio(BaseModel): @@ -77,86 +174,168 @@ class Audio(BaseModel): output: Optional[AudioOutput] = None -class Tool(BaseModel): - description: Optional[str] = None - """ - The description of the function, including guidance on when and how to call it, - and guidance about what to tell the user when calling (if anything). +ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] + + +class ToolMcpToolAllowedToolsMcpToolFilter(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. """ - name: Optional[str] = None - """The name of the function.""" + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" - parameters: Optional[object] = None - """Parameters of the function in JSON Schema.""" - type: Optional[Literal["function"]] = None - """The type of the tool, i.e. `function`.""" +ToolMcpToolAllowedTools: TypeAlias = Union[List[str], ToolMcpToolAllowedToolsMcpToolFilter, None] -class TracingTracingConfiguration(BaseModel): - group_id: Optional[str] = None +class ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. """ - The group id to attach to this trace to enable filtering and grouping in the - traces dashboard. + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class ToolMcpToolRequireApprovalMcpToolApprovalFilterNever(BaseModel): + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. """ - metadata: Optional[object] = None + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class ToolMcpToolRequireApprovalMcpToolApprovalFilter(BaseModel): + always: Optional[ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[ToolMcpToolRequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +ToolMcpToolRequireApproval: TypeAlias = Union[ + ToolMcpToolRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None +] + + +class ToolMcpTool(BaseModel): + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_tools: Optional[ToolMcpToolAllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None """ - The arbitrary metadata to attach to this trace to enable filtering in the traces - dashboard. + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. """ - workflow_name: Optional[str] = None - """The name of the workflow to attach to this trace. - - This is used to name the trace in the traces dashboard. + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url` or `connector_id` must be provided. Learn more about service + connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` """ + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ -Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration] + require_approval: Optional[ToolMcpToolRequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" -class TurnDetection(BaseModel): - prefix_padding_ms: Optional[int] = None - """Amount of audio to include before the VAD detected speech (in milliseconds). + server_url: Optional[str] = None + """The URL for the MCP server. - Defaults to 300ms. + One of `server_url` or `connector_id` must be provided. """ - silence_duration_ms: Optional[int] = None - """Duration of silence to detect speech stop (in milliseconds). - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. +Tool: TypeAlias = Union[Models, ToolMcpTool] + + +class TracingTracingConfiguration(BaseModel): + group_id: Optional[str] = None + """ + The group id to attach to this trace to enable filtering and grouping in the + Traces Dashboard. """ - threshold: Optional[float] = None - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + metadata: Optional[object] = None + """ + The arbitrary metadata to attach to this trace to enable filtering in the Traces + Dashboard. + """ - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. + workflow_name: Optional[str] = None + """The name of the workflow to attach to this trace. + + This is used to name the trace in the Traces Dashboard. """ - type: Optional[str] = None - """Type of turn detection, only `server_vad` is currently supported.""" +Tracing: TypeAlias = Union[Literal["auto"], TracingTracingConfiguration, None] -class RealtimeSessionCreateResponse(BaseModel): - id: Optional[str] = None - """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" +class RealtimeSessionCreateResponse(BaseModel): audio: Optional[Audio] = None - """Configuration for input and output audio for the session.""" + """Configuration for input and output audio.""" - expires_at: Optional[int] = None - """Expiration timestamp for the session, in seconds since epoch.""" + client_secret: Optional[RealtimeSessionClientSecret] = None + """Ephemeral key returned by the API.""" include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None """Additional fields to include in server outputs. - - `item.input_audio_transcription.logprobs`: Include logprobs for input audio - transcription. + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. """ instructions: Optional[str] = None @@ -182,41 +361,60 @@ class RealtimeSessionCreateResponse(BaseModel): `inf` for the maximum available tokens for a given model. Defaults to `inf`. """ - model: Optional[str] = None + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + None, + ] = None """The Realtime model used for this session.""" - object: Optional[str] = None - """The object type. Always `realtime.session`.""" - output_modalities: Optional[List[Literal["text", "audio"]]] = None """The set of modalities the model can respond with. - To disable audio, set this to ["text"]. + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. + """ + + prompt: Optional[ResponsePrompt] = None + """Reference to a prompt template and its variables. + + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ - tool_choice: Optional[str] = None + tool_choice: Optional[ToolChoice] = None """How the model chooses tools. - Options are `auto`, `none`, `required`, or specify a function. + Provide one of the string modes or force a specific function/MCP tool. """ tools: Optional[List[Tool]] = None - """Tools (functions) available to the model.""" + """Tools available to the model.""" tracing: Optional[Tracing] = None - """Configuration options for tracing. - - Set to null to disable tracing. Once tracing is enabled for a session, the - configuration cannot be modified. + """ + Realtime API can write session traces to the + [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once + tracing is enabled for a session, the configuration cannot be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. """ - turn_detection: Optional[TurnDetection] = None - """Configuration for turn detection. - - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. + truncation: Optional[RealtimeTruncation] = None """ + Controls how the realtime conversation is truncated prior to model inference. + The default is `auto`. + """ + + type: Optional[Literal["realtime"]] = None + """The type of session to create. Always `realtime` for the Realtime API.""" diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py index ea4b8c4d43..700b548fe2 100644 --- a/src/openai/types/realtime/realtime_tools_config_param.py +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -6,11 +6,11 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .models_param import ModelsParam __all__ = [ "RealtimeToolsConfigParam", "RealtimeToolsConfigUnionParam", - "Function", "Mcp", "McpAllowedTools", "McpAllowedToolsMcpToolFilter", @@ -21,23 +21,6 @@ ] -class Function(TypedDict, total=False): - description: str - """ - The description of the function, including guidance on when and how to call it, - and guidance about what to tell the user when calling (if anything). - """ - - name: str - """The name of the function.""" - - parameters: object - """Parameters of the function in JSON Schema.""" - - type: Literal["function"] - """The type of the tool, i.e. `function`.""" - - class McpAllowedToolsMcpToolFilter(TypedDict, total=False): read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -155,6 +138,6 @@ class Mcp(TypedDict, total=False): """ -RealtimeToolsConfigUnionParam: TypeAlias = Union[Function, Mcp] +RealtimeToolsConfigUnionParam: TypeAlias = Union[ModelsParam, Mcp] RealtimeToolsConfigParam: TypeAlias = List[RealtimeToolsConfigUnionParam] diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py index 16b1557743..8a064d78d4 100644 --- a/src/openai/types/realtime/realtime_tools_config_union.py +++ b/src/openai/types/realtime/realtime_tools_config_union.py @@ -3,12 +3,12 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias +from .models import Models from ..._utils import PropertyInfo from ..._models import BaseModel __all__ = [ "RealtimeToolsConfigUnion", - "Function", "Mcp", "McpAllowedTools", "McpAllowedToolsMcpToolFilter", @@ -19,23 +19,6 @@ ] -class Function(BaseModel): - description: Optional[str] = None - """ - The description of the function, including guidance on when and how to call it, - and guidance about what to tell the user when calling (if anything). - """ - - name: Optional[str] = None - """The name of the function.""" - - parameters: Optional[object] = None - """Parameters of the function in JSON Schema.""" - - type: Optional[Literal["function"]] = None - """The type of the tool, i.e. `function`.""" - - class McpAllowedToolsMcpToolFilter(BaseModel): read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -155,4 +138,4 @@ class Mcp(BaseModel): """ -RealtimeToolsConfigUnion: TypeAlias = Annotated[Union[Function, Mcp], PropertyInfo(discriminator="type")] +RealtimeToolsConfigUnion: TypeAlias = Annotated[Union[Models, Mcp], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py index 21b4d07752..179ad040d9 100644 --- a/src/openai/types/realtime/realtime_tools_config_union_param.py +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -6,10 +6,10 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .models_param import ModelsParam __all__ = [ "RealtimeToolsConfigUnionParam", - "Function", "Mcp", "McpAllowedTools", "McpAllowedToolsMcpToolFilter", @@ -20,23 +20,6 @@ ] -class Function(TypedDict, total=False): - description: str - """ - The description of the function, including guidance on when and how to call it, - and guidance about what to tell the user when calling (if anything). - """ - - name: str - """The name of the function.""" - - parameters: object - """Parameters of the function in JSON Schema.""" - - type: Literal["function"] - """The type of the tool, i.e. `function`.""" - - class McpAllowedToolsMcpToolFilter(TypedDict, total=False): read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -154,4 +137,4 @@ class Mcp(TypedDict, total=False): """ -RealtimeToolsConfigUnionParam: TypeAlias = Union[Function, Mcp] +RealtimeToolsConfigUnionParam: TypeAlias = Union[ModelsParam, Mcp] diff --git a/src/openai/types/realtime/realtime_tracing_config.py b/src/openai/types/realtime/realtime_tracing_config.py index 1de24d6e5f..1c46de7928 100644 --- a/src/openai/types/realtime/realtime_tracing_config.py +++ b/src/openai/types/realtime/realtime_tracing_config.py @@ -12,19 +12,19 @@ class TracingConfiguration(BaseModel): group_id: Optional[str] = None """ The group id to attach to this trace to enable filtering and grouping in the - traces dashboard. + Traces Dashboard. """ metadata: Optional[object] = None """ - The arbitrary metadata to attach to this trace to enable filtering in the traces - dashboard. + The arbitrary metadata to attach to this trace to enable filtering in the Traces + Dashboard. """ workflow_name: Optional[str] = None """The name of the workflow to attach to this trace. - This is used to name the trace in the traces dashboard. + This is used to name the trace in the Traces Dashboard. """ diff --git a/src/openai/types/realtime/realtime_tracing_config_param.py b/src/openai/types/realtime/realtime_tracing_config_param.py index 3a35c6f7fa..fd9e266244 100644 --- a/src/openai/types/realtime/realtime_tracing_config_param.py +++ b/src/openai/types/realtime/realtime_tracing_config_param.py @@ -12,19 +12,19 @@ class TracingConfiguration(TypedDict, total=False): group_id: str """ The group id to attach to this trace to enable filtering and grouping in the - traces dashboard. + Traces Dashboard. """ metadata: object """ - The arbitrary metadata to attach to this trace to enable filtering in the traces - dashboard. + The arbitrary metadata to attach to this trace to enable filtering in the Traces + Dashboard. """ workflow_name: str """The name of the workflow to attach to this trace. - This is used to name the trace in the traces dashboard. + This is used to name the trace in the Traces Dashboard. """ diff --git a/src/openai/types/realtime/realtime_transcription_session_audio.py b/src/openai/types/realtime/realtime_transcription_session_audio.py new file mode 100644 index 0000000000..a5506947f1 --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_audio.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .realtime_transcription_session_audio_input import RealtimeTranscriptionSessionAudioInput + +__all__ = ["RealtimeTranscriptionSessionAudio"] + + +class RealtimeTranscriptionSessionAudio(BaseModel): + input: Optional[RealtimeTranscriptionSessionAudioInput] = None diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input.py b/src/openai/types/realtime/realtime_transcription_session_audio_input.py new file mode 100644 index 0000000000..0ae92959aa --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .audio_transcription import AudioTranscription +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_transcription_session_audio_input_turn_detection import ( + RealtimeTranscriptionSessionAudioInputTurnDetection, +) + +__all__ = ["RealtimeTranscriptionSessionAudioInput", "NoiseReduction"] + + +class NoiseReduction(BaseModel): + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeTranscriptionSessionAudioInput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The PCM audio format. Only a 24kHz sample rate is supported.""" + + noise_reduction: Optional[NoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: Optional[AudioTranscription] = None + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: Optional[RealtimeTranscriptionSessionAudioInputTurnDetection] = None + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py new file mode 100644 index 0000000000..a8263789dc --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .noise_reduction_type import NoiseReductionType +from .audio_transcription_param import AudioTranscriptionParam +from .realtime_audio_formats_param import RealtimeAudioFormatsParam +from .realtime_transcription_session_audio_input_turn_detection_param import ( + RealtimeTranscriptionSessionAudioInputTurnDetectionParam, +) + +__all__ = ["RealtimeTranscriptionSessionAudioInputParam", "NoiseReduction"] + + +class NoiseReduction(TypedDict, total=False): + type: NoiseReductionType + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class RealtimeTranscriptionSessionAudioInputParam(TypedDict, total=False): + format: RealtimeAudioFormatsParam + """The PCM audio format. Only a 24kHz sample rate is supported.""" + + noise_reduction: NoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + transcription: AudioTranscriptionParam + """ + Configuration for input audio transcription, defaults to off and can be set to + `null` to turn off once on. Input audio transcription is not native to the + model, since the model consumes audio directly. Transcription runs + asynchronously through + [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) + and should be treated as guidance of input audio content rather than precisely + what the model heard. The client can optionally set the language and prompt for + transcription, these offer additional guidance to the transcription service. + """ + + turn_detection: RealtimeTranscriptionSessionAudioInputTurnDetectionParam + """Configuration for turn detection, ether Server VAD or Semantic VAD. + + This can be set to `null` to turn off, in which case the client must manually + trigger model response. Server VAD means that the model will detect the start + and end of speech based on audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction + with VAD) to semantically estimate whether the user has finished speaking, then + dynamically sets a timeout based on this probability. For example, if user audio + trails off with "uhhm", the model will score a low probability of turn end and + wait longer for the user to continue speaking. This can be useful for more + natural conversations, but may have a higher latency. + """ diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py new file mode 100644 index 0000000000..0cac36f7a3 --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetection"] + + +class RealtimeTranscriptionSessionAudioInputTurnDetection(BaseModel): + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + idle_timeout_ms: Optional[int] = None + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: Optional[float] = None + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Optional[Literal["server_vad", "semantic_vad"]] = None + """Type of turn detection.""" diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py new file mode 100644 index 0000000000..e76dc9a8fe --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetectionParam"] + + +class RealtimeTranscriptionSessionAudioInputTurnDetectionParam(TypedDict, total=False): + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. + """ + + idle_timeout_ms: Optional[int] + """ + Optional idle timeout after which turn detection will auto-timeout when no + additional audio is received. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + prefix_padding_ms: int + """Used only for `server_vad` mode. + + Amount of audio to include before the VAD detected speech (in milliseconds). + Defaults to 300ms. + """ + + silence_duration_ms: int + """Used only for `server_vad` mode. + + Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. + With shorter values the model will respond more quickly, but may jump in on + short pauses from the user. + """ + + threshold: float + """Used only for `server_vad` mode. + + Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher + threshold will require louder audio to activate the model, and thus might + perform better in noisy environments. + """ + + type: Literal["server_vad", "semantic_vad"] + """Type of turn detection.""" diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_param.py new file mode 100644 index 0000000000..1503a606d3 --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_audio_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .realtime_transcription_session_audio_input_param import RealtimeTranscriptionSessionAudioInputParam + +__all__ = ["RealtimeTranscriptionSessionAudioParam"] + + +class RealtimeTranscriptionSessionAudioParam(TypedDict, total=False): + input: RealtimeTranscriptionSessionAudioInputParam diff --git a/src/openai/types/realtime/realtime_transcription_session_client_secret.py b/src/openai/types/realtime/realtime_transcription_session_client_secret.py new file mode 100644 index 0000000000..0cfde4c0a2 --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_client_secret.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["RealtimeTranscriptionSessionClientSecret"] + + +class RealtimeTranscriptionSessionClientSecret(BaseModel): + expires_at: int + """Timestamp for when the token expires. + + Currently, all tokens expire after one minute. + """ + + value: str + """ + Ephemeral key usable in client environments to authenticate connections to the + Realtime API. Use this in client-side environments rather than a standard API + token, which should only be used server-side. + """ diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request.py b/src/openai/types/realtime/realtime_transcription_session_create_request.py index d67bc92708..102f2b14fb 100644 --- a/src/openai/types/realtime/realtime_transcription_session_create_request.py +++ b/src/openai/types/realtime/realtime_transcription_session_create_request.py @@ -1,128 +1,27 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel +from .realtime_transcription_session_audio import RealtimeTranscriptionSessionAudio -__all__ = [ - "RealtimeTranscriptionSessionCreateRequest", - "InputAudioNoiseReduction", - "InputAudioTranscription", - "TurnDetection", -] - - -class InputAudioNoiseReduction(BaseModel): - type: Optional[Literal["near_field", "far_field"]] = None - """Type of noise reduction. - - `near_field` is for close-talking microphones such as headphones, `far_field` is - for far-field microphones such as laptop or conference room microphones. - """ - - -class InputAudioTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None - """ - The model to use for transcription, current options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1`. - """ - - prompt: Optional[str] = None - """ - An optional text to guide the model's style or continue a previous audio - segment. For `whisper-1`, the - [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". - """ - - -class TurnDetection(BaseModel): - prefix_padding_ms: Optional[int] = None - """Amount of audio to include before the VAD detected speech (in milliseconds). - - Defaults to 300ms. - """ - - silence_duration_ms: Optional[int] = None - """Duration of silence to detect speech stop (in milliseconds). - - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. - """ - - threshold: Optional[float] = None - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. - - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. - """ - - type: Optional[Literal["server_vad"]] = None - """Type of turn detection. - - Only `server_vad` is currently supported for transcription sessions. - """ +__all__ = ["RealtimeTranscriptionSessionCreateRequest"] class RealtimeTranscriptionSessionCreateRequest(BaseModel): - model: Union[str, Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe"]] - """ID of the model to use. - - The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` - (which is powered by our open source Whisper V2 model). - """ - type: Literal["transcription"] """The type of session to create. Always `transcription` for transcription sessions. """ - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None - """The set of items to include in the transcription. Current available items are: - - - `item.input_audio_transcription.logprobs` - """ + audio: Optional[RealtimeTranscriptionSessionAudio] = None + """Configuration for input and output audio.""" - input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of input audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must - be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian - byte order. - """ - - input_audio_noise_reduction: Optional[InputAudioNoiseReduction] = None - """Configuration for input audio noise reduction. - - This can be set to `null` to turn off. Noise reduction filters audio added to - the input audio buffer before it is sent to VAD and the model. Filtering the - audio can improve VAD and turn detection accuracy (reducing false positives) and - model performance by improving perception of the input audio. - """ - - input_audio_transcription: Optional[InputAudioTranscription] = None - """Configuration for input audio transcription. - - The client can optionally set the language and prompt for transcription, these - offer additional guidance to the transcription service. - """ - - turn_detection: Optional[TurnDetection] = None - """Configuration for turn detection. + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. """ diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request_param.py b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py index 405f0c5f2c..80cbe2d414 100644 --- a/src/openai/types/realtime/realtime_transcription_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py @@ -2,127 +2,27 @@ from __future__ import annotations -from typing import List, Union +from typing import List from typing_extensions import Literal, Required, TypedDict -__all__ = [ - "RealtimeTranscriptionSessionCreateRequestParam", - "InputAudioNoiseReduction", - "InputAudioTranscription", - "TurnDetection", -] +from .realtime_transcription_session_audio_param import RealtimeTranscriptionSessionAudioParam - -class InputAudioNoiseReduction(TypedDict, total=False): - type: Literal["near_field", "far_field"] - """Type of noise reduction. - - `near_field` is for close-talking microphones such as headphones, `far_field` is - for far-field microphones such as laptop or conference room microphones. - """ - - -class InputAudioTranscription(TypedDict, total=False): - language: str - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"] - """ - The model to use for transcription, current options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1`. - """ - - prompt: str - """ - An optional text to guide the model's style or continue a previous audio - segment. For `whisper-1`, the - [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". - """ - - -class TurnDetection(TypedDict, total=False): - prefix_padding_ms: int - """Amount of audio to include before the VAD detected speech (in milliseconds). - - Defaults to 300ms. - """ - - silence_duration_ms: int - """Duration of silence to detect speech stop (in milliseconds). - - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. - """ - - threshold: float - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. - - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. - """ - - type: Literal["server_vad"] - """Type of turn detection. - - Only `server_vad` is currently supported for transcription sessions. - """ +__all__ = ["RealtimeTranscriptionSessionCreateRequestParam"] class RealtimeTranscriptionSessionCreateRequestParam(TypedDict, total=False): - model: Required[Union[str, Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe"]]] - """ID of the model to use. - - The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` - (which is powered by our open source Whisper V2 model). - """ - type: Required[Literal["transcription"]] """The type of session to create. Always `transcription` for transcription sessions. """ - include: List[Literal["item.input_audio_transcription.logprobs"]] - """The set of items to include in the transcription. Current available items are: - - - `item.input_audio_transcription.logprobs` - """ + audio: RealtimeTranscriptionSessionAudioParam + """Configuration for input and output audio.""" - input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of input audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must - be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian - byte order. - """ - - input_audio_noise_reduction: InputAudioNoiseReduction - """Configuration for input audio noise reduction. - - This can be set to `null` to turn off. Noise reduction filters audio added to - the input audio buffer before it is sent to VAD and the model. Filtering the - audio can improve VAD and turn detection accuracy (reducing false positives) and - model performance by improving perception of the input audio. - """ - - input_audio_transcription: InputAudioTranscription - """Configuration for input audio transcription. - - The client can optionally set the language and prompt for transcription, these - offer additional guidance to the transcription service. - """ - - turn_detection: TurnDetection - """Configuration for turn detection. + include: List[Literal["item.input_audio_transcription.logprobs"]] + """Additional fields to include in server outputs. - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. """ diff --git a/src/openai/types/realtime/realtime_transcription_session_create_response.py b/src/openai/types/realtime/realtime_transcription_session_create_response.py new file mode 100644 index 0000000000..a08538aa8f --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_create_response.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .realtime_transcription_session_client_secret import RealtimeTranscriptionSessionClientSecret +from .realtime_transcription_session_turn_detection import RealtimeTranscriptionSessionTurnDetection +from .realtime_transcription_session_input_audio_transcription import ( + RealtimeTranscriptionSessionInputAudioTranscription, +) + +__all__ = ["RealtimeTranscriptionSessionCreateResponse"] + + +class RealtimeTranscriptionSessionCreateResponse(BaseModel): + client_secret: RealtimeTranscriptionSessionClientSecret + """Ephemeral key returned by the API. + + Only present when the session is created on the server via REST API. + """ + + input_audio_format: Optional[str] = None + """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" + + input_audio_transcription: Optional[RealtimeTranscriptionSessionInputAudioTranscription] = None + """Configuration of the transcription model.""" + + modalities: Optional[List[Literal["text", "audio"]]] = None + """The set of modalities the model can respond with. + + To disable audio, set this to ["text"]. + """ + + turn_detection: Optional[RealtimeTranscriptionSessionTurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ diff --git a/src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py b/src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py new file mode 100644 index 0000000000..52254bed33 --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeTranscriptionSessionInputAudioTranscription"] + + +class RealtimeTranscriptionSessionInputAudioTranscription(BaseModel): + language: Optional[str] = None + """The language of the input audio. + + Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + """ + + model: Optional[Literal["whisper-1", "gpt-4o-transcribe-latest", "gpt-4o-mini-transcribe", "gpt-4o-transcribe"]] = ( + None + ) + """The model to use for transcription. + + Current options are `whisper-1`, `gpt-4o-transcribe-latest`, + `gpt-4o-mini-transcribe`, and `gpt-4o-transcribe`. + """ + + prompt: Optional[str] = None + """ + An optional text to guide the model's style or continue a previous audio + segment. For `whisper-1`, the + [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). + For `gpt-4o-transcribe` models, the prompt is a free text string, for example + "expect words related to technology". + """ diff --git a/src/openai/types/realtime/realtime_transcription_session_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py new file mode 100644 index 0000000000..f5da31ce77 --- /dev/null +++ b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["RealtimeTranscriptionSessionTurnDetection"] + + +class RealtimeTranscriptionSessionTurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[str] = None + """Type of turn detection, only `server_vad` is currently supported.""" diff --git a/src/openai/types/realtime/realtime_truncation.py b/src/openai/types/realtime/realtime_truncation.py index 4687e3da56..515f869071 100644 --- a/src/openai/types/realtime/realtime_truncation.py +++ b/src/openai/types/realtime/realtime_truncation.py @@ -1,22 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union, Optional +from typing import Union from typing_extensions import Literal, TypeAlias -from ..._models import BaseModel +from .realtime_truncation_retention_ratio import RealtimeTruncationRetentionRatio -__all__ = ["RealtimeTruncation", "RetentionRatioTruncation"] +__all__ = ["RealtimeTruncation"] - -class RetentionRatioTruncation(BaseModel): - retention_ratio: float - """Fraction of pre-instruction conversation tokens to retain (0.0 - 1.0).""" - - type: Literal["retention_ratio"] - """Use retention ratio truncation.""" - - post_instructions_token_limit: Optional[int] = None - """Optional cap on tokens allowed after the instructions.""" - - -RealtimeTruncation: TypeAlias = Union[Literal["auto", "disabled"], RetentionRatioTruncation] +RealtimeTruncation: TypeAlias = Union[Literal["auto", "disabled"], RealtimeTruncationRetentionRatio] diff --git a/src/openai/types/realtime/realtime_truncation_param.py b/src/openai/types/realtime/realtime_truncation_param.py index edc88ea685..5e42b27418 100644 --- a/src/openai/types/realtime/realtime_truncation_param.py +++ b/src/openai/types/realtime/realtime_truncation_param.py @@ -2,21 +2,11 @@ from __future__ import annotations -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing import Union +from typing_extensions import Literal, TypeAlias -__all__ = ["RealtimeTruncationParam", "RetentionRatioTruncation"] +from .realtime_truncation_retention_ratio_param import RealtimeTruncationRetentionRatioParam +__all__ = ["RealtimeTruncationParam"] -class RetentionRatioTruncation(TypedDict, total=False): - retention_ratio: Required[float] - """Fraction of pre-instruction conversation tokens to retain (0.0 - 1.0).""" - - type: Required[Literal["retention_ratio"]] - """Use retention ratio truncation.""" - - post_instructions_token_limit: Optional[int] - """Optional cap on tokens allowed after the instructions.""" - - -RealtimeTruncationParam: TypeAlias = Union[Literal["auto", "disabled"], RetentionRatioTruncation] +RealtimeTruncationParam: TypeAlias = Union[Literal["auto", "disabled"], RealtimeTruncationRetentionRatioParam] diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio.py b/src/openai/types/realtime/realtime_truncation_retention_ratio.py new file mode 100644 index 0000000000..b40427244e --- /dev/null +++ b/src/openai/types/realtime/realtime_truncation_retention_ratio.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["RealtimeTruncationRetentionRatio"] + + +class RealtimeTruncationRetentionRatio(BaseModel): + retention_ratio: float + """ + Fraction of post-instruction conversation tokens to retain (0.0 - 1.0) when the + conversation exceeds the input token limit. + """ + + type: Literal["retention_ratio"] + """Use retention ratio truncation.""" diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py new file mode 100644 index 0000000000..b65d65666a --- /dev/null +++ b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RealtimeTruncationRetentionRatioParam"] + + +class RealtimeTruncationRetentionRatioParam(TypedDict, total=False): + retention_ratio: Required[float] + """ + Fraction of post-instruction conversation tokens to retain (0.0 - 1.0) when the + conversation exceeds the input token limit. + """ + + type: Required[Literal["retention_ratio"]] + """Use retention ratio truncation.""" diff --git a/src/openai/types/realtime/response_create_event.py b/src/openai/types/realtime/response_create_event.py index a37045eab1..75a08ee460 100644 --- a/src/openai/types/realtime/response_create_event.py +++ b/src/openai/types/realtime/response_create_event.py @@ -1,126 +1,12 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional -from typing_extensions import Literal, TypeAlias +from typing import Optional +from typing_extensions import Literal from ..._models import BaseModel -from ..shared.metadata import Metadata -from .conversation_item import ConversationItem -from ..responses.response_prompt import ResponsePrompt -from ..responses.tool_choice_mcp import ToolChoiceMcp -from ..responses.tool_choice_options import ToolChoiceOptions -from ..responses.tool_choice_function import ToolChoiceFunction +from .realtime_response_create_params import RealtimeResponseCreateParams -__all__ = ["ResponseCreateEvent", "Response", "ResponseToolChoice", "ResponseTool"] - -ResponseToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] - - -class ResponseTool(BaseModel): - description: Optional[str] = None - """ - The description of the function, including guidance on when and how to call it, - and guidance about what to tell the user when calling (if anything). - """ - - name: Optional[str] = None - """The name of the function.""" - - parameters: Optional[object] = None - """Parameters of the function in JSON Schema.""" - - type: Optional[Literal["function"]] = None - """The type of the tool, i.e. `function`.""" - - -class Response(BaseModel): - conversation: Union[str, Literal["auto", "none"], None] = None - """Controls which conversation the response is added to. - - Currently supports `auto` and `none`, with `auto` as the default value. The - `auto` value means that the contents of the response will be added to the - default conversation. Set this to `none` to create an out-of-band response which - will not add items to default conversation. - """ - - input: Optional[List[ConversationItem]] = None - """Input items to include in the prompt for the model. - - Using this field creates a new context for this Response instead of using the - default conversation. An empty array `[]` will clear the context for this - Response. Note that this can include references to items from the default - conversation. - """ - - instructions: Optional[str] = None - """The default system instructions (i.e. - - system message) prepended to model calls. This field allows the client to guide - the model on desired responses. The model can be instructed on response content - and format, (e.g. "be extremely succinct", "act friendly", "here are examples of - good responses") and on audio behavior (e.g. "talk quickly", "inject emotion - into your voice", "laugh frequently"). The instructions are not guaranteed to be - followed by the model, but they provide guidance to the model on the desired - behavior. - - Note that the server sets default instructions which will be used if this field - is not set and are visible in the `session.created` event at the start of the - session. - """ - - max_output_tokens: Union[int, Literal["inf"], None] = None - """ - Maximum number of output tokens for a single assistant response, inclusive of - tool calls. Provide an integer between 1 and 4096 to limit output tokens, or - `inf` for the maximum available tokens for a given model. Defaults to `inf`. - """ - - metadata: Optional[Metadata] = None - """Set of 16 key-value pairs that can be attached to an object. - - This can be useful for storing additional information about the object in a - structured format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - """ - - modalities: Optional[List[Literal["text", "audio"]]] = None - """The set of modalities the model can respond with. - - To disable audio, set this to ["text"]. - """ - - output_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" - - prompt: Optional[ResponsePrompt] = None - """Reference to a prompt template and its variables. - - [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). - """ - - temperature: Optional[float] = None - """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" - - tool_choice: Optional[ResponseToolChoice] = None - """How the model chooses tools. - - Provide one of the string modes or force a specific function/MCP tool. - """ - - tools: Optional[List[ResponseTool]] = None - """Tools (functions) available to the model.""" - - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None - ] = None - """The voice the model uses to respond. - - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, and `verse`. - """ +__all__ = ["ResponseCreateEvent"] class ResponseCreateEvent(BaseModel): @@ -130,5 +16,5 @@ class ResponseCreateEvent(BaseModel): event_id: Optional[str] = None """Optional client-generated ID used to identify this event.""" - response: Optional[Response] = None + response: Optional[RealtimeResponseCreateParams] = None """Create a new Realtime response with these parameters""" diff --git a/src/openai/types/realtime/response_create_event_param.py b/src/openai/types/realtime/response_create_event_param.py index f941c4ca9c..e5dd46d9b6 100644 --- a/src/openai/types/realtime/response_create_event_param.py +++ b/src/openai/types/realtime/response_create_event_param.py @@ -2,124 +2,11 @@ from __future__ import annotations -from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict +from typing_extensions import Literal, Required, TypedDict -from ..shared_params.metadata import Metadata -from .conversation_item_param import ConversationItemParam -from ..responses.tool_choice_options import ToolChoiceOptions -from ..responses.response_prompt_param import ResponsePromptParam -from ..responses.tool_choice_mcp_param import ToolChoiceMcpParam -from ..responses.tool_choice_function_param import ToolChoiceFunctionParam +from .realtime_response_create_params_param import RealtimeResponseCreateParamsParam -__all__ = ["ResponseCreateEventParam", "Response", "ResponseToolChoice", "ResponseTool"] - -ResponseToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunctionParam, ToolChoiceMcpParam] - - -class ResponseTool(TypedDict, total=False): - description: str - """ - The description of the function, including guidance on when and how to call it, - and guidance about what to tell the user when calling (if anything). - """ - - name: str - """The name of the function.""" - - parameters: object - """Parameters of the function in JSON Schema.""" - - type: Literal["function"] - """The type of the tool, i.e. `function`.""" - - -class Response(TypedDict, total=False): - conversation: Union[str, Literal["auto", "none"]] - """Controls which conversation the response is added to. - - Currently supports `auto` and `none`, with `auto` as the default value. The - `auto` value means that the contents of the response will be added to the - default conversation. Set this to `none` to create an out-of-band response which - will not add items to default conversation. - """ - - input: Iterable[ConversationItemParam] - """Input items to include in the prompt for the model. - - Using this field creates a new context for this Response instead of using the - default conversation. An empty array `[]` will clear the context for this - Response. Note that this can include references to items from the default - conversation. - """ - - instructions: str - """The default system instructions (i.e. - - system message) prepended to model calls. This field allows the client to guide - the model on desired responses. The model can be instructed on response content - and format, (e.g. "be extremely succinct", "act friendly", "here are examples of - good responses") and on audio behavior (e.g. "talk quickly", "inject emotion - into your voice", "laugh frequently"). The instructions are not guaranteed to be - followed by the model, but they provide guidance to the model on the desired - behavior. - - Note that the server sets default instructions which will be used if this field - is not set and are visible in the `session.created` event at the start of the - session. - """ - - max_output_tokens: Union[int, Literal["inf"]] - """ - Maximum number of output tokens for a single assistant response, inclusive of - tool calls. Provide an integer between 1 and 4096 to limit output tokens, or - `inf` for the maximum available tokens for a given model. Defaults to `inf`. - """ - - metadata: Optional[Metadata] - """Set of 16 key-value pairs that can be attached to an object. - - This can be useful for storing additional information about the object in a - structured format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. - """ - - modalities: List[Literal["text", "audio"]] - """The set of modalities the model can respond with. - - To disable audio, set this to ["text"]. - """ - - output_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" - - prompt: Optional[ResponsePromptParam] - """Reference to a prompt template and its variables. - - [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). - """ - - temperature: float - """Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.""" - - tool_choice: ResponseToolChoice - """How the model chooses tools. - - Provide one of the string modes or force a specific function/MCP tool. - """ - - tools: Iterable[ResponseTool] - """Tools (functions) available to the model.""" - - voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] - """The voice the model uses to respond. - - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, and `verse`. - """ +__all__ = ["ResponseCreateEventParam"] class ResponseCreateEventParam(TypedDict, total=False): @@ -129,5 +16,5 @@ class ResponseCreateEventParam(TypedDict, total=False): event_id: str """Optional client-generated ID used to identify this event.""" - response: Response + response: RealtimeResponseCreateParamsParam """Create a new Realtime response with these parameters""" diff --git a/src/openai/types/realtime/session_created_event.py b/src/openai/types/realtime/session_created_event.py index 51f75700f0..b5caad35d7 100644 --- a/src/openai/types/realtime/session_created_event.py +++ b/src/openai/types/realtime/session_created_event.py @@ -1,19 +1,23 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing import Union +from typing_extensions import Literal, TypeAlias from ..._models import BaseModel -from .realtime_session import RealtimeSession +from .realtime_session_create_request import RealtimeSessionCreateRequest +from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest -__all__ = ["SessionCreatedEvent"] +__all__ = ["SessionCreatedEvent", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequest, RealtimeTranscriptionSessionCreateRequest] class SessionCreatedEvent(BaseModel): event_id: str """The unique ID of the server event.""" - session: RealtimeSession - """Realtime session object.""" + session: Session + """The session configuration.""" type: Literal["session.created"] """The event type, must be `session.created`.""" diff --git a/src/openai/types/realtime/session_update_event.py b/src/openai/types/realtime/session_update_event.py index 00a4377f96..2e226162c4 100644 --- a/src/openai/types/realtime/session_update_event.py +++ b/src/openai/types/realtime/session_update_event.py @@ -1,20 +1,31 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, TypeAlias from ..._models import BaseModel from .realtime_session_create_request import RealtimeSessionCreateRequest +from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest -__all__ = ["SessionUpdateEvent"] +__all__ = ["SessionUpdateEvent", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequest, RealtimeTranscriptionSessionCreateRequest] class SessionUpdateEvent(BaseModel): - session: RealtimeSessionCreateRequest - """Realtime session object configuration.""" + session: Session + """Update the Realtime session. + + Choose either a realtime session or a transcription session. + """ type: Literal["session.update"] """The event type, must be `session.update`.""" event_id: Optional[str] = None - """Optional client-generated ID used to identify this event.""" + """Optional client-generated ID used to identify this event. + + This is an arbitrary string that a client may assign. It will be passed back if + there is an error with the event, but the corresponding `session.updated` event + will not include it. + """ diff --git a/src/openai/types/realtime/session_update_event_param.py b/src/openai/types/realtime/session_update_event_param.py index 79ff05f729..5962361431 100644 --- a/src/openai/types/realtime/session_update_event_param.py +++ b/src/openai/types/realtime/session_update_event_param.py @@ -2,19 +2,31 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .realtime_session_create_request_param import RealtimeSessionCreateRequestParam +from .realtime_transcription_session_create_request_param import RealtimeTranscriptionSessionCreateRequestParam -__all__ = ["SessionUpdateEventParam"] +__all__ = ["SessionUpdateEventParam", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequestParam, RealtimeTranscriptionSessionCreateRequestParam] class SessionUpdateEventParam(TypedDict, total=False): - session: Required[RealtimeSessionCreateRequestParam] - """Realtime session object configuration.""" + session: Required[Session] + """Update the Realtime session. + + Choose either a realtime session or a transcription session. + """ type: Required[Literal["session.update"]] """The event type, must be `session.update`.""" event_id: str - """Optional client-generated ID used to identify this event.""" + """Optional client-generated ID used to identify this event. + + This is an arbitrary string that a client may assign. It will be passed back if + there is an error with the event, but the corresponding `session.updated` event + will not include it. + """ diff --git a/src/openai/types/realtime/session_updated_event.py b/src/openai/types/realtime/session_updated_event.py index b8a5972f6e..eb7ee0332d 100644 --- a/src/openai/types/realtime/session_updated_event.py +++ b/src/openai/types/realtime/session_updated_event.py @@ -1,19 +1,23 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing import Union +from typing_extensions import Literal, TypeAlias from ..._models import BaseModel -from .realtime_session import RealtimeSession +from .realtime_session_create_request import RealtimeSessionCreateRequest +from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest -__all__ = ["SessionUpdatedEvent"] +__all__ = ["SessionUpdatedEvent", "Session"] + +Session: TypeAlias = Union[RealtimeSessionCreateRequest, RealtimeTranscriptionSessionCreateRequest] class SessionUpdatedEvent(BaseModel): event_id: str """The unique ID of the server event.""" - session: RealtimeSession - """Realtime session object.""" + session: Session + """The session configuration.""" type: Literal["session.updated"] """The event type, must be `session.updated`.""" diff --git a/src/openai/types/realtime/transcription_session_created.py b/src/openai/types/realtime/transcription_session_created.py index 1d34d152d7..c358c5e8b0 100644 --- a/src/openai/types/realtime/transcription_session_created.py +++ b/src/openai/types/realtime/transcription_session_created.py @@ -1,105 +1,24 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel +from .realtime_transcription_session_create_response import RealtimeTranscriptionSessionCreateResponse -__all__ = [ - "TranscriptionSessionCreated", - "Session", - "SessionAudio", - "SessionAudioInput", - "SessionAudioInputNoiseReduction", - "SessionAudioInputTranscription", - "SessionAudioInputTurnDetection", -] - - -class SessionAudioInputNoiseReduction(BaseModel): - type: Optional[Literal["near_field", "far_field"]] = None - - -class SessionAudioInputTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None - """The model to use for transcription. - - Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. - """ - - prompt: Optional[str] = None - """An optional text to guide the model's style or continue a previous audio - segment. - - The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. - """ - - -class SessionAudioInputTurnDetection(BaseModel): - prefix_padding_ms: Optional[int] = None - - silence_duration_ms: Optional[int] = None - - threshold: Optional[float] = None - - type: Optional[str] = None - """Type of turn detection, only `server_vad` is currently supported.""" - - -class SessionAudioInput(BaseModel): - format: Optional[str] = None - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" - - noise_reduction: Optional[SessionAudioInputNoiseReduction] = None - """Configuration for input audio noise reduction.""" - - transcription: Optional[SessionAudioInputTranscription] = None - """Configuration of the transcription model.""" - - turn_detection: Optional[SessionAudioInputTurnDetection] = None - """Configuration for turn detection.""" - - -class SessionAudio(BaseModel): - input: Optional[SessionAudioInput] = None - - -class Session(BaseModel): - id: Optional[str] = None - """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" - - audio: Optional[SessionAudio] = None - """Configuration for input audio for the session.""" - - expires_at: Optional[int] = None - """Expiration timestamp for the session, in seconds since epoch.""" - - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None - """Additional fields to include in server outputs. - - - `item.input_audio_transcription.logprobs`: Include logprobs for input audio - transcription. - """ - - object: Optional[str] = None - """The object type. Always `realtime.transcription_session`.""" +__all__ = ["TranscriptionSessionCreated"] class TranscriptionSessionCreated(BaseModel): event_id: str """The unique ID of the server event.""" - session: Session - """A Realtime transcription session configuration object.""" + session: RealtimeTranscriptionSessionCreateResponse + """A new Realtime transcription session configuration. + + When a session is created on the server via REST API, the session object also + contains an ephemeral key. Default TTL for keys is 10 minutes. This property is + not present when a session is updated via the WebSocket API. + """ type: Literal["transcription_session.created"] """The event type, must be `transcription_session.created`.""" diff --git a/src/openai/types/realtime/transcription_session_update.py b/src/openai/types/realtime/transcription_session_update.py index c8f5b9eb4a..0faff9cb57 100644 --- a/src/openai/types/realtime/transcription_session_update.py +++ b/src/openai/types/realtime/transcription_session_update.py @@ -1,16 +1,94 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel -from .realtime_transcription_session_create_request import RealtimeTranscriptionSessionCreateRequest +from .audio_transcription import AudioTranscription +from .noise_reduction_type import NoiseReductionType -__all__ = ["TranscriptionSessionUpdate"] +__all__ = ["TranscriptionSessionUpdate", "Session", "SessionInputAudioNoiseReduction", "SessionTurnDetection"] + + +class SessionInputAudioNoiseReduction(BaseModel): + type: Optional[NoiseReductionType] = None + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionTurnDetection(BaseModel): + prefix_padding_ms: Optional[int] = None + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: Optional[int] = None + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: Optional[float] = None + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Optional[Literal["server_vad"]] = None + """Type of turn detection. + + Only `server_vad` is currently supported for transcription sessions. + """ + + +class Session(BaseModel): + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """The set of items to include in the transcription. + + Current available items are: `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: Optional[SessionInputAudioNoiseReduction] = None + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: Optional[AudioTranscription] = None + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + turn_detection: Optional[SessionTurnDetection] = None + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ class TranscriptionSessionUpdate(BaseModel): - session: RealtimeTranscriptionSessionCreateRequest + session: Session """Realtime transcription session object configuration.""" type: Literal["transcription_session.update"] diff --git a/src/openai/types/realtime/transcription_session_update_param.py b/src/openai/types/realtime/transcription_session_update_param.py index f2e66efaa0..55c67798b6 100644 --- a/src/openai/types/realtime/transcription_session_update_param.py +++ b/src/openai/types/realtime/transcription_session_update_param.py @@ -2,15 +2,94 @@ from __future__ import annotations +from typing import List from typing_extensions import Literal, Required, TypedDict -from .realtime_transcription_session_create_request_param import RealtimeTranscriptionSessionCreateRequestParam +from .noise_reduction_type import NoiseReductionType +from .audio_transcription_param import AudioTranscriptionParam -__all__ = ["TranscriptionSessionUpdateParam"] +__all__ = ["TranscriptionSessionUpdateParam", "Session", "SessionInputAudioNoiseReduction", "SessionTurnDetection"] + + +class SessionInputAudioNoiseReduction(TypedDict, total=False): + type: NoiseReductionType + """Type of noise reduction. + + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. + """ + + +class SessionTurnDetection(TypedDict, total=False): + prefix_padding_ms: int + """Amount of audio to include before the VAD detected speech (in milliseconds). + + Defaults to 300ms. + """ + + silence_duration_ms: int + """Duration of silence to detect speech stop (in milliseconds). + + Defaults to 500ms. With shorter values the model will respond more quickly, but + may jump in on short pauses from the user. + """ + + threshold: float + """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. + + A higher threshold will require louder audio to activate the model, and thus + might perform better in noisy environments. + """ + + type: Literal["server_vad"] + """Type of turn detection. + + Only `server_vad` is currently supported for transcription sessions. + """ + + +class Session(TypedDict, total=False): + include: List[Literal["item.input_audio_transcription.logprobs"]] + """The set of items to include in the transcription. + + Current available items are: `item.input_audio_transcription.logprobs` + """ + + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] + """The format of input audio. + + Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must + be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian + byte order. + """ + + input_audio_noise_reduction: SessionInputAudioNoiseReduction + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. Noise reduction filters audio added to + the input audio buffer before it is sent to VAD and the model. Filtering the + audio can improve VAD and turn detection accuracy (reducing false positives) and + model performance by improving perception of the input audio. + """ + + input_audio_transcription: AudioTranscriptionParam + """Configuration for input audio transcription. + + The client can optionally set the language and prompt for transcription, these + offer additional guidance to the transcription service. + """ + + turn_detection: SessionTurnDetection + """Configuration for turn detection. + + Can be set to `null` to turn off. Server VAD means that the model will detect + the start and end of speech based on audio volume and respond at the end of user + speech. + """ class TranscriptionSessionUpdateParam(TypedDict, total=False): - session: Required[RealtimeTranscriptionSessionCreateRequestParam] + session: Required[Session] """Realtime transcription session object configuration.""" type: Required[Literal["transcription_session.update"]] diff --git a/src/openai/types/realtime/transcription_session_updated_event.py b/src/openai/types/realtime/transcription_session_updated_event.py index 9abd1d20be..f6a52a12f3 100644 --- a/src/openai/types/realtime/transcription_session_updated_event.py +++ b/src/openai/types/realtime/transcription_session_updated_event.py @@ -1,105 +1,24 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel +from .realtime_transcription_session_create_response import RealtimeTranscriptionSessionCreateResponse -__all__ = [ - "TranscriptionSessionUpdatedEvent", - "Session", - "SessionAudio", - "SessionAudioInput", - "SessionAudioInputNoiseReduction", - "SessionAudioInputTranscription", - "SessionAudioInputTurnDetection", -] - - -class SessionAudioInputNoiseReduction(BaseModel): - type: Optional[Literal["near_field", "far_field"]] = None - - -class SessionAudioInputTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Optional[Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "whisper-1"]] = None - """The model to use for transcription. - - Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`. - """ - - prompt: Optional[str] = None - """An optional text to guide the model's style or continue a previous audio - segment. - - The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. - """ - - -class SessionAudioInputTurnDetection(BaseModel): - prefix_padding_ms: Optional[int] = None - - silence_duration_ms: Optional[int] = None - - threshold: Optional[float] = None - - type: Optional[str] = None - """Type of turn detection, only `server_vad` is currently supported.""" - - -class SessionAudioInput(BaseModel): - format: Optional[str] = None - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" - - noise_reduction: Optional[SessionAudioInputNoiseReduction] = None - """Configuration for input audio noise reduction.""" - - transcription: Optional[SessionAudioInputTranscription] = None - """Configuration of the transcription model.""" - - turn_detection: Optional[SessionAudioInputTurnDetection] = None - """Configuration for turn detection.""" - - -class SessionAudio(BaseModel): - input: Optional[SessionAudioInput] = None - - -class Session(BaseModel): - id: Optional[str] = None - """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" - - audio: Optional[SessionAudio] = None - """Configuration for input audio for the session.""" - - expires_at: Optional[int] = None - """Expiration timestamp for the session, in seconds since epoch.""" - - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None - """Additional fields to include in server outputs. - - - `item.input_audio_transcription.logprobs`: Include logprobs for input audio - transcription. - """ - - object: Optional[str] = None - """The object type. Always `realtime.transcription_session`.""" +__all__ = ["TranscriptionSessionUpdatedEvent"] class TranscriptionSessionUpdatedEvent(BaseModel): event_id: str """The unique ID of the server event.""" - session: Session - """A Realtime transcription session configuration object.""" + session: RealtimeTranscriptionSessionCreateResponse + """A new Realtime transcription session configuration. + + When a session is created on the server via REST API, the session object also + contains an ephemeral key. Default TTL for keys is 10 minutes. This property is + not present when a session is updated via the WebSocket API. + """ type: Literal["transcription_session.updated"] """The event type, must be `transcription_session.updated`.""" diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py index c477268ee6..b7bb0e5aa7 100644 --- a/tests/api_resources/realtime/test_client_secrets.py +++ b/tests/api_resources/realtime/test_client_secrets.py @@ -30,11 +30,13 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "seconds": 10, }, session={ - "model": "string", "type": "realtime", "audio": { "input": { - "format": "pcm16", + "format": { + "rate": 24000, + "type": "audio/pcm", + }, "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", @@ -53,27 +55,24 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: }, }, "output": { - "format": "pcm16", + "format": { + "rate": 24000, + "type": "audio/pcm", + }, "speed": 0.25, "voice": "ash", }, }, - "client_secret": { - "expires_after": { - "anchor": "created_at", - "seconds": 0, - } - }, "include": ["item.input_audio_transcription.logprobs"], "instructions": "instructions", "max_output_tokens": 0, + "model": "string", "output_modalities": ["text"], "prompt": { "id": "id", "variables": {"foo": "string"}, "version": "version", }, - "temperature": 0, "tool_choice": "none", "tools": [ { @@ -128,11 +127,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "seconds": 10, }, session={ - "model": "string", "type": "realtime", "audio": { "input": { - "format": "pcm16", + "format": { + "rate": 24000, + "type": "audio/pcm", + }, "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", @@ -151,27 +152,24 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> }, }, "output": { - "format": "pcm16", + "format": { + "rate": 24000, + "type": "audio/pcm", + }, "speed": 0.25, "voice": "ash", }, }, - "client_secret": { - "expires_after": { - "anchor": "created_at", - "seconds": 0, - } - }, "include": ["item.input_audio_transcription.logprobs"], "instructions": "instructions", "max_output_tokens": 0, + "model": "string", "output_modalities": ["text"], "prompt": { "id": "id", "variables": {"foo": "string"}, "version": "version", }, - "temperature": 0, "tool_choice": "none", "tools": [ { From 847ff0b83841d9262ba0d9c4fdf46f0478004ad0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:03:41 -0400 Subject: [PATCH 104/408] release: 1.107.1 (#2619) * chore(api): fix realtime GA types * release: 1.107.1 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +- CHANGELOG.md | 8 ++ api.md | 5 +- pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/realtime/realtime.py | 38 +------ src/openai/types/realtime/__init__.py | 14 +-- .../realtime/client_secret_create_response.py | 7 +- .../realtime_audio_input_turn_detection.py | 2 +- ...altime_audio_input_turn_detection_param.py | 2 +- .../types/realtime/realtime_client_event.py | 2 - .../realtime/realtime_client_event_param.py | 2 - .../{models.py => realtime_function_tool.py} | 4 +- ...ram.py => realtime_function_tool_param.py} | 4 +- .../realtime_response_create_params.py | 4 +- .../realtime_response_create_params_param.py | 4 +- .../types/realtime/realtime_server_event.py | 4 - .../realtime_session_create_response.py | 18 ++-- .../realtime/realtime_tools_config_param.py | 4 +- .../realtime/realtime_tools_config_union.py | 4 +- .../realtime_tools_config_union_param.py | 4 +- ...ime_transcription_session_client_secret.py | 20 ---- ...e_transcription_session_create_response.py | 61 ++++++++---- ...ption_session_input_audio_transcription.py | 36 ------- .../realtime/transcription_session_created.py | 24 ----- .../realtime/transcription_session_update.py | 98 ------------------ .../transcription_session_update_param.py | 99 ------------------- .../transcription_session_updated_event.py | 24 ----- 29 files changed, 94 insertions(+), 410 deletions(-) rename src/openai/types/realtime/{models.py => realtime_function_tool.py} (89%) rename src/openai/types/realtime/{models_param.py => realtime_function_tool_param.py} (85%) delete mode 100644 src/openai/types/realtime/realtime_transcription_session_client_secret.py delete mode 100644 src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py delete mode 100644 src/openai/types/realtime/transcription_session_created.py delete mode 100644 src/openai/types/realtime/transcription_session_update.py delete mode 100644 src/openai/types/realtime/transcription_session_update_param.py delete mode 100644 src/openai/types/realtime/transcription_session_updated_event.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 12cec28d56..25880b2e7b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.107.0" + ".": "1.107.1" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 36a3c7f587..2aa16be875 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7807ec6037efcee1af7decbfd3974a42b761fb6c6a71b4050fe43484d7fcbac4.yml -openapi_spec_hash: da6851e3891ad2659a50ed6a736fd32a -config_hash: 74d955cdc2377213f5268ea309090f6c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-16cb18bed32bae8c5840fb39a1bf664026cc40463ad0c487dcb0df1bd3d72db0.yml +openapi_spec_hash: 4cb51b22f98dee1a90bc7add82d1d132 +config_hash: 930dac3aa861344867e4ac84f037b5df diff --git a/CHANGELOG.md b/CHANGELOG.md index 76d5dcb2dd..19eab7da7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.107.1 (2025-09-10) + +Full Changelog: [v1.107.0...v1.107.1](https://github.com/openai/openai-python/compare/v1.107.0...v1.107.1) + +### Chores + +* **api:** fix realtime GA types ([570fc5a](https://github.com/openai/openai-python/commit/570fc5a28ada665fd658b24675361680cfeb086f)) + ## 1.107.0 (2025-09-08) Full Changelog: [v1.106.1...v1.107.0](https://github.com/openai/openai-python/compare/v1.106.1...v1.107.0) diff --git a/api.md b/api.md index 7c947fffe1..73b8427387 100644 --- a/api.md +++ b/api.md @@ -892,7 +892,6 @@ from openai.types.realtime import ( McpListToolsCompleted, McpListToolsFailed, McpListToolsInProgress, - Models, NoiseReductionType, OutputAudioBufferClearEvent, RateLimitsUpdatedEvent, @@ -909,6 +908,7 @@ from openai.types.realtime import ( RealtimeConversationItemUserMessage, RealtimeError, RealtimeErrorEvent, + RealtimeFunctionTool, RealtimeMcpApprovalRequest, RealtimeMcpApprovalResponse, RealtimeMcpListTools, @@ -961,7 +961,6 @@ from openai.types.realtime import ( SessionCreatedEvent, SessionUpdateEvent, SessionUpdatedEvent, - TranscriptionSessionCreated, TranscriptionSessionUpdate, TranscriptionSessionUpdatedEvent, ) @@ -975,9 +974,7 @@ Types: from openai.types.realtime import ( RealtimeSessionClientSecret, RealtimeSessionCreateResponse, - RealtimeTranscriptionSessionClientSecret, RealtimeTranscriptionSessionCreateResponse, - RealtimeTranscriptionSessionInputAudioTranscription, RealtimeTranscriptionSessionTurnDetection, ClientSecretCreateResponse, ) diff --git a/pyproject.toml b/pyproject.toml index 5c3985cc7c..326dc5a004 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.107.0" +version = "1.107.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 06826fc4de..f337b21cd5 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.107.0" # x-release-please-version +__version__ = "1.107.1" # x-release-please-version diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 81e6dc54f5..64fca72915 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -32,7 +32,7 @@ ClientSecretsWithStreamingResponse, AsyncClientSecretsWithStreamingResponse, ) -from ...types.realtime import session_update_event_param, transcription_session_update_param +from ...types.realtime import session_update_event_param from ...types.websocket_connection_options import WebsocketConnectionOptions from ...types.realtime.realtime_client_event import RealtimeClientEvent from ...types.realtime.realtime_server_event import RealtimeServerEvent @@ -199,7 +199,6 @@ class AsyncRealtimeConnection: input_audio_buffer: AsyncRealtimeInputAudioBufferResource conversation: AsyncRealtimeConversationResource output_audio_buffer: AsyncRealtimeOutputAudioBufferResource - transcription_session: AsyncRealtimeTranscriptionSessionResource _connection: AsyncWebsocketConnection @@ -211,7 +210,6 @@ def __init__(self, connection: AsyncWebsocketConnection) -> None: self.input_audio_buffer = AsyncRealtimeInputAudioBufferResource(self) self.conversation = AsyncRealtimeConversationResource(self) self.output_audio_buffer = AsyncRealtimeOutputAudioBufferResource(self) - self.transcription_session = AsyncRealtimeTranscriptionSessionResource(self) async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: """ @@ -381,7 +379,6 @@ class RealtimeConnection: input_audio_buffer: RealtimeInputAudioBufferResource conversation: RealtimeConversationResource output_audio_buffer: RealtimeOutputAudioBufferResource - transcription_session: RealtimeTranscriptionSessionResource _connection: WebsocketConnection @@ -393,7 +390,6 @@ def __init__(self, connection: WebsocketConnection) -> None: self.input_audio_buffer = RealtimeInputAudioBufferResource(self) self.conversation = RealtimeConversationResource(self) self.output_audio_buffer = RealtimeOutputAudioBufferResource(self) - self.transcription_session = RealtimeTranscriptionSessionResource(self) def __iter__(self) -> Iterator[RealtimeServerEvent]: """ @@ -565,8 +561,7 @@ def update(self, *, session: session_update_event_param.Session, event_id: str | """ Send this event to update the session’s configuration. The client may send this event at any time to update any field - except for `voice` and `model`. `voice` can be updated only if there have been no other - audio outputs yet. + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. When the server receives a `session.update`, it will respond with a `session.updated` event showing the full, effective configuration. @@ -800,19 +795,6 @@ def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: ) -class RealtimeTranscriptionSessionResource(BaseRealtimeConnectionResource): - def update( - self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN - ) -> None: - """Send this event to update a transcription session.""" - self._connection.send( - cast( - RealtimeClientEventParam, - strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), - ) - ) - - class BaseAsyncRealtimeConnectionResource: def __init__(self, connection: AsyncRealtimeConnection) -> None: self._connection = connection @@ -825,8 +807,7 @@ async def update( """ Send this event to update the session’s configuration. The client may send this event at any time to update any field - except for `voice` and `model`. `voice` can be updated only if there have been no other - audio outputs yet. + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. When the server receives a `session.update`, it will respond with a `session.updated` event showing the full, effective configuration. @@ -1058,16 +1039,3 @@ async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: await self._connection.send( cast(RealtimeClientEventParam, strip_not_given({"type": "output_audio_buffer.clear", "event_id": event_id})) ) - - -class AsyncRealtimeTranscriptionSessionResource(BaseAsyncRealtimeConnectionResource): - async def update( - self, *, session: transcription_session_update_param.Session, event_id: str | NotGiven = NOT_GIVEN - ) -> None: - """Send this event to update a transcription session.""" - await self._connection.send( - cast( - RealtimeClientEventParam, - strip_not_given({"type": "transcription_session.update", "session": session, "event_id": event_id}), - ) - ) diff --git a/src/openai/types/realtime/__init__.py b/src/openai/types/realtime/__init__.py index 6873ba6a2a..2d947c8a2f 100644 --- a/src/openai/types/realtime/__init__.py +++ b/src/openai/types/realtime/__init__.py @@ -2,8 +2,6 @@ from __future__ import annotations -from .models import Models as Models -from .models_param import ModelsParam as ModelsParam from .realtime_error import RealtimeError as RealtimeError from .conversation_item import ConversationItem as ConversationItem from .realtime_response import RealtimeResponse as RealtimeResponse @@ -25,6 +23,7 @@ from .session_updated_event import SessionUpdatedEvent as SessionUpdatedEvent from .conversation_item_done import ConversationItemDone as ConversationItemDone from .realtime_audio_formats import RealtimeAudioFormats as RealtimeAudioFormats +from .realtime_function_tool import RealtimeFunctionTool as RealtimeFunctionTool from .realtime_mcp_tool_call import RealtimeMcpToolCall as RealtimeMcpToolCall from .realtime_mcphttp_error import RealtimeMcphttpError as RealtimeMcphttpError from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent @@ -60,15 +59,14 @@ from .response_mcp_call_completed import ResponseMcpCallCompleted as ResponseMcpCallCompleted from .realtime_audio_config_output import RealtimeAudioConfigOutput as RealtimeAudioConfigOutput from .realtime_audio_formats_param import RealtimeAudioFormatsParam as RealtimeAudioFormatsParam +from .realtime_function_tool_param import RealtimeFunctionToolParam as RealtimeFunctionToolParam from .realtime_mcp_tool_call_param import RealtimeMcpToolCallParam as RealtimeMcpToolCallParam from .realtime_mcphttp_error_param import RealtimeMcphttpErrorParam as RealtimeMcphttpErrorParam -from .transcription_session_update import TranscriptionSessionUpdate as TranscriptionSessionUpdate from .client_secret_create_response import ClientSecretCreateResponse as ClientSecretCreateResponse from .realtime_mcp_approval_request import RealtimeMcpApprovalRequest as RealtimeMcpApprovalRequest from .realtime_mcp_list_tools_param import RealtimeMcpListToolsParam as RealtimeMcpListToolsParam from .realtime_tracing_config_param import RealtimeTracingConfigParam as RealtimeTracingConfigParam from .response_mcp_call_in_progress import ResponseMcpCallInProgress as ResponseMcpCallInProgress -from .transcription_session_created import TranscriptionSessionCreated as TranscriptionSessionCreated from .conversation_item_create_event import ConversationItemCreateEvent as ConversationItemCreateEvent from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent @@ -100,11 +98,9 @@ from .response_mcp_call_arguments_delta import ResponseMcpCallArgumentsDelta as ResponseMcpCallArgumentsDelta from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent as InputAudioBufferCommittedEvent from .realtime_audio_config_output_param import RealtimeAudioConfigOutputParam as RealtimeAudioConfigOutputParam -from .transcription_session_update_param import TranscriptionSessionUpdateParam as TranscriptionSessionUpdateParam from .realtime_audio_input_turn_detection import RealtimeAudioInputTurnDetection as RealtimeAudioInputTurnDetection from .realtime_mcp_approval_request_param import RealtimeMcpApprovalRequestParam as RealtimeMcpApprovalRequestParam from .realtime_truncation_retention_ratio import RealtimeTruncationRetentionRatio as RealtimeTruncationRetentionRatio -from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent as TranscriptionSessionUpdatedEvent from .conversation_item_create_event_param import ConversationItemCreateEventParam as ConversationItemCreateEventParam from .conversation_item_delete_event_param import ConversationItemDeleteEventParam as ConversationItemDeleteEventParam from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam as InputAudioBufferClearEventParam @@ -181,9 +177,6 @@ from .realtime_response_usage_output_token_details import ( RealtimeResponseUsageOutputTokenDetails as RealtimeResponseUsageOutputTokenDetails, ) -from .realtime_transcription_session_client_secret import ( - RealtimeTranscriptionSessionClientSecret as RealtimeTranscriptionSessionClientSecret, -) from .response_function_call_arguments_delta_event import ( ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, ) @@ -229,9 +222,6 @@ from .conversation_item_input_audio_transcription_failed_event import ( ConversationItemInputAudioTranscriptionFailedEvent as ConversationItemInputAudioTranscriptionFailedEvent, ) -from .realtime_transcription_session_input_audio_transcription import ( - RealtimeTranscriptionSessionInputAudioTranscription as RealtimeTranscriptionSessionInputAudioTranscription, -) from .realtime_transcription_session_audio_input_turn_detection import ( RealtimeTranscriptionSessionAudioInputTurnDetection as RealtimeTranscriptionSessionAudioInputTurnDetection, ) diff --git a/src/openai/types/realtime/client_secret_create_response.py b/src/openai/types/realtime/client_secret_create_response.py index 8d61be3ab7..2aed66a25b 100644 --- a/src/openai/types/realtime/client_secret_create_response.py +++ b/src/openai/types/realtime/client_secret_create_response.py @@ -1,15 +1,18 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union -from typing_extensions import TypeAlias +from typing_extensions import Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel from .realtime_session_create_response import RealtimeSessionCreateResponse from .realtime_transcription_session_create_response import RealtimeTranscriptionSessionCreateResponse __all__ = ["ClientSecretCreateResponse", "Session"] -Session: TypeAlias = Union[RealtimeSessionCreateResponse, RealtimeTranscriptionSessionCreateResponse] +Session: TypeAlias = Annotated[ + Union[RealtimeSessionCreateResponse, RealtimeTranscriptionSessionCreateResponse], PropertyInfo(discriminator="type") +] class ClientSecretCreateResponse(BaseModel): diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_audio_input_turn_detection.py index ea9423f6a1..1c736ab2b7 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection.py @@ -27,7 +27,7 @@ class RealtimeAudioInputTurnDetection(BaseModel): idle_timeout_ms: Optional[int] = None """ Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. + additional audio is received and emits a `timeout_triggered` event. """ interrupt_response: Optional[bool] = None diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py index ec398f52e6..79cabec708 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py @@ -27,7 +27,7 @@ class RealtimeAudioInputTurnDetectionParam(TypedDict, total=False): idle_timeout_ms: Optional[int] """ Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. + additional audio is received and emits a `timeout_triggered` event. """ interrupt_response: bool diff --git a/src/openai/types/realtime/realtime_client_event.py b/src/openai/types/realtime/realtime_client_event.py index 8c2c95e849..3b1c348daa 100644 --- a/src/openai/types/realtime/realtime_client_event.py +++ b/src/openai/types/realtime/realtime_client_event.py @@ -7,7 +7,6 @@ from .session_update_event import SessionUpdateEvent from .response_cancel_event import ResponseCancelEvent from .response_create_event import ResponseCreateEvent -from .transcription_session_update import TranscriptionSessionUpdate from .conversation_item_create_event import ConversationItemCreateEvent from .conversation_item_delete_event import ConversationItemDeleteEvent from .input_audio_buffer_clear_event import InputAudioBufferClearEvent @@ -32,7 +31,6 @@ ResponseCancelEvent, ResponseCreateEvent, SessionUpdateEvent, - TranscriptionSessionUpdate, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/realtime/realtime_client_event_param.py b/src/openai/types/realtime/realtime_client_event_param.py index 8e042dd64b..cda5766e2a 100644 --- a/src/openai/types/realtime/realtime_client_event_param.py +++ b/src/openai/types/realtime/realtime_client_event_param.py @@ -8,7 +8,6 @@ from .session_update_event_param import SessionUpdateEventParam from .response_cancel_event_param import ResponseCancelEventParam from .response_create_event_param import ResponseCreateEventParam -from .transcription_session_update_param import TranscriptionSessionUpdateParam from .conversation_item_create_event_param import ConversationItemCreateEventParam from .conversation_item_delete_event_param import ConversationItemDeleteEventParam from .input_audio_buffer_clear_event_param import InputAudioBufferClearEventParam @@ -32,5 +31,4 @@ ResponseCancelEventParam, ResponseCreateEventParam, SessionUpdateEventParam, - TranscriptionSessionUpdateParam, ] diff --git a/src/openai/types/realtime/models.py b/src/openai/types/realtime/realtime_function_tool.py similarity index 89% rename from src/openai/types/realtime/models.py rename to src/openai/types/realtime/realtime_function_tool.py index d4827538a3..48dbf9929d 100644 --- a/src/openai/types/realtime/models.py +++ b/src/openai/types/realtime/realtime_function_tool.py @@ -5,10 +5,10 @@ from ..._models import BaseModel -__all__ = ["Models"] +__all__ = ["RealtimeFunctionTool"] -class Models(BaseModel): +class RealtimeFunctionTool(BaseModel): description: Optional[str] = None """ The description of the function, including guidance on when and how to call it, diff --git a/src/openai/types/realtime/models_param.py b/src/openai/types/realtime/realtime_function_tool_param.py similarity index 85% rename from src/openai/types/realtime/models_param.py rename to src/openai/types/realtime/realtime_function_tool_param.py index 1db2d7e464..f42e3e497c 100644 --- a/src/openai/types/realtime/models_param.py +++ b/src/openai/types/realtime/realtime_function_tool_param.py @@ -4,10 +4,10 @@ from typing_extensions import Literal, TypedDict -__all__ = ["ModelsParam"] +__all__ = ["RealtimeFunctionToolParam"] -class ModelsParam(TypedDict, total=False): +class RealtimeFunctionToolParam(TypedDict, total=False): description: str """ The description of the function, including guidance on when and how to call it, diff --git a/src/openai/types/realtime/realtime_response_create_params.py b/src/openai/types/realtime/realtime_response_create_params.py index 3b5a8907a1..4dfd1fd386 100644 --- a/src/openai/types/realtime/realtime_response_create_params.py +++ b/src/openai/types/realtime/realtime_response_create_params.py @@ -3,10 +3,10 @@ from typing import List, Union, Optional from typing_extensions import Literal, TypeAlias -from .models import Models from ..._models import BaseModel from ..shared.metadata import Metadata from .conversation_item import ConversationItem +from .realtime_function_tool import RealtimeFunctionTool from ..responses.response_prompt import ResponsePrompt from ..responses.tool_choice_mcp import ToolChoiceMcp from ..responses.tool_choice_options import ToolChoiceOptions @@ -18,7 +18,7 @@ ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunction, ToolChoiceMcp] -Tool: TypeAlias = Union[Models, RealtimeResponseCreateMcpTool] +Tool: TypeAlias = Union[RealtimeFunctionTool, RealtimeResponseCreateMcpTool] class RealtimeResponseCreateParams(BaseModel): diff --git a/src/openai/types/realtime/realtime_response_create_params_param.py b/src/openai/types/realtime/realtime_response_create_params_param.py index 6800d36a31..eceffcccb7 100644 --- a/src/openai/types/realtime/realtime_response_create_params_param.py +++ b/src/openai/types/realtime/realtime_response_create_params_param.py @@ -5,9 +5,9 @@ from typing import List, Union, Iterable, Optional from typing_extensions import Literal, TypeAlias, TypedDict -from .models_param import ModelsParam from ..shared_params.metadata import Metadata from .conversation_item_param import ConversationItemParam +from .realtime_function_tool_param import RealtimeFunctionToolParam from ..responses.tool_choice_options import ToolChoiceOptions from ..responses.response_prompt_param import ResponsePromptParam from ..responses.tool_choice_mcp_param import ToolChoiceMcpParam @@ -19,7 +19,7 @@ ToolChoice: TypeAlias = Union[ToolChoiceOptions, ToolChoiceFunctionParam, ToolChoiceMcpParam] -Tool: TypeAlias = Union[ModelsParam, RealtimeResponseCreateMcpToolParam] +Tool: TypeAlias = Union[RealtimeFunctionToolParam, RealtimeResponseCreateMcpToolParam] class RealtimeResponseCreateParamsParam(TypedDict, total=False): diff --git a/src/openai/types/realtime/realtime_server_event.py b/src/openai/types/realtime/realtime_server_event.py index 8094bcfa96..1605b81a97 100644 --- a/src/openai/types/realtime/realtime_server_event.py +++ b/src/openai/types/realtime/realtime_server_event.py @@ -25,7 +25,6 @@ from .response_audio_delta_event import ResponseAudioDeltaEvent from .response_mcp_call_completed import ResponseMcpCallCompleted from .response_mcp_call_in_progress import ResponseMcpCallInProgress -from .transcription_session_created import TranscriptionSessionCreated from .conversation_item_created_event import ConversationItemCreatedEvent from .conversation_item_deleted_event import ConversationItemDeletedEvent from .response_output_item_done_event import ResponseOutputItemDoneEvent @@ -37,7 +36,6 @@ from .response_content_part_added_event import ResponseContentPartAddedEvent from .response_mcp_call_arguments_delta import ResponseMcpCallArgumentsDelta from .input_audio_buffer_committed_event import InputAudioBufferCommittedEvent -from .transcription_session_updated_event import TranscriptionSessionUpdatedEvent from .input_audio_buffer_timeout_triggered import InputAudioBufferTimeoutTriggered from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent @@ -137,8 +135,6 @@ class OutputAudioBufferCleared(BaseModel): ResponseTextDoneEvent, SessionCreatedEvent, SessionUpdatedEvent, - TranscriptionSessionUpdatedEvent, - TranscriptionSessionCreated, OutputAudioBufferStarted, OutputAudioBufferStopped, OutputAudioBufferCleared, diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 9c10b84588..7779f07a6e 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -3,12 +3,12 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, TypeAlias -from .models import Models from ..._models import BaseModel from .audio_transcription import AudioTranscription from .realtime_truncation import RealtimeTruncation from .noise_reduction_type import NoiseReductionType from .realtime_audio_formats import RealtimeAudioFormats +from .realtime_function_tool import RealtimeFunctionTool from ..responses.response_prompt import ResponsePrompt from ..responses.tool_choice_mcp import ToolChoiceMcp from ..responses.tool_choice_options import ToolChoiceOptions @@ -64,7 +64,7 @@ class AudioInputTurnDetection(BaseModel): idle_timeout_ms: Optional[int] = None """ Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. + additional audio is received and emits a `timeout_triggered` event. """ interrupt_response: Optional[bool] = None @@ -298,7 +298,7 @@ class ToolMcpTool(BaseModel): """ -Tool: TypeAlias = Union[Models, ToolMcpTool] +Tool: TypeAlias = Union[RealtimeFunctionTool, ToolMcpTool] class TracingTracingConfiguration(BaseModel): @@ -325,12 +325,15 @@ class TracingTracingConfiguration(BaseModel): class RealtimeSessionCreateResponse(BaseModel): + client_secret: RealtimeSessionClientSecret + """Ephemeral key returned by the API.""" + + type: Literal["realtime"] + """The type of session to create. Always `realtime` for the Realtime API.""" + audio: Optional[Audio] = None """Configuration for input and output audio.""" - client_secret: Optional[RealtimeSessionClientSecret] = None - """Ephemeral key returned by the API.""" - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None """Additional fields to include in server outputs. @@ -415,6 +418,3 @@ class RealtimeSessionCreateResponse(BaseModel): Controls how the realtime conversation is truncated prior to model inference. The default is `auto`. """ - - type: Optional[Literal["realtime"]] = None - """The type of session to create. Always `realtime` for the Realtime API.""" diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py index 700b548fe2..630fc74691 100644 --- a/src/openai/types/realtime/realtime_tools_config_param.py +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -6,7 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr -from .models_param import ModelsParam +from .realtime_function_tool_param import RealtimeFunctionToolParam __all__ = [ "RealtimeToolsConfigParam", @@ -138,6 +138,6 @@ class Mcp(TypedDict, total=False): """ -RealtimeToolsConfigUnionParam: TypeAlias = Union[ModelsParam, Mcp] +RealtimeToolsConfigUnionParam: TypeAlias = Union[RealtimeFunctionToolParam, Mcp] RealtimeToolsConfigParam: TypeAlias = List[RealtimeToolsConfigUnionParam] diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py index 8a064d78d4..e7126ed60d 100644 --- a/src/openai/types/realtime/realtime_tools_config_union.py +++ b/src/openai/types/realtime/realtime_tools_config_union.py @@ -3,9 +3,9 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias -from .models import Models from ..._utils import PropertyInfo from ..._models import BaseModel +from .realtime_function_tool import RealtimeFunctionTool __all__ = [ "RealtimeToolsConfigUnion", @@ -138,4 +138,4 @@ class Mcp(BaseModel): """ -RealtimeToolsConfigUnion: TypeAlias = Annotated[Union[Models, Mcp], PropertyInfo(discriminator="type")] +RealtimeToolsConfigUnion: TypeAlias = Annotated[Union[RealtimeFunctionTool, Mcp], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py index 179ad040d9..9ee58fdbe6 100644 --- a/src/openai/types/realtime/realtime_tools_config_union_param.py +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -6,7 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr -from .models_param import ModelsParam +from .realtime_function_tool_param import RealtimeFunctionToolParam __all__ = [ "RealtimeToolsConfigUnionParam", @@ -137,4 +137,4 @@ class Mcp(TypedDict, total=False): """ -RealtimeToolsConfigUnionParam: TypeAlias = Union[ModelsParam, Mcp] +RealtimeToolsConfigUnionParam: TypeAlias = Union[RealtimeFunctionToolParam, Mcp] diff --git a/src/openai/types/realtime/realtime_transcription_session_client_secret.py b/src/openai/types/realtime/realtime_transcription_session_client_secret.py deleted file mode 100644 index 0cfde4c0a2..0000000000 --- a/src/openai/types/realtime/realtime_transcription_session_client_secret.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["RealtimeTranscriptionSessionClientSecret"] - - -class RealtimeTranscriptionSessionClientSecret(BaseModel): - expires_at: int - """Timestamp for when the token expires. - - Currently, all tokens expire after one minute. - """ - - value: str - """ - Ephemeral key usable in client environments to authenticate connections to the - Realtime API. Use this in client-side environments rather than a standard API - token, which should only be used server-side. - """ diff --git a/src/openai/types/realtime/realtime_transcription_session_create_response.py b/src/openai/types/realtime/realtime_transcription_session_create_response.py index a08538aa8f..301af1ac3f 100644 --- a/src/openai/types/realtime/realtime_transcription_session_create_response.py +++ b/src/openai/types/realtime/realtime_transcription_session_create_response.py @@ -4,33 +4,32 @@ from typing_extensions import Literal from ..._models import BaseModel -from .realtime_transcription_session_client_secret import RealtimeTranscriptionSessionClientSecret +from .audio_transcription import AudioTranscription +from .noise_reduction_type import NoiseReductionType +from .realtime_audio_formats import RealtimeAudioFormats from .realtime_transcription_session_turn_detection import RealtimeTranscriptionSessionTurnDetection -from .realtime_transcription_session_input_audio_transcription import ( - RealtimeTranscriptionSessionInputAudioTranscription, -) -__all__ = ["RealtimeTranscriptionSessionCreateResponse"] +__all__ = ["RealtimeTranscriptionSessionCreateResponse", "Audio", "AudioInput", "AudioInputNoiseReduction"] -class RealtimeTranscriptionSessionCreateResponse(BaseModel): - client_secret: RealtimeTranscriptionSessionClientSecret - """Ephemeral key returned by the API. +class AudioInputNoiseReduction(BaseModel): + type: Optional[NoiseReductionType] = None + """Type of noise reduction. - Only present when the session is created on the server via REST API. + `near_field` is for close-talking microphones such as headphones, `far_field` is + for far-field microphones such as laptop or conference room microphones. """ - input_audio_format: Optional[str] = None - """The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.""" - input_audio_transcription: Optional[RealtimeTranscriptionSessionInputAudioTranscription] = None - """Configuration of the transcription model.""" +class AudioInput(BaseModel): + format: Optional[RealtimeAudioFormats] = None + """The PCM audio format. Only a 24kHz sample rate is supported.""" - modalities: Optional[List[Literal["text", "audio"]]] = None - """The set of modalities the model can respond with. + noise_reduction: Optional[AudioInputNoiseReduction] = None + """Configuration for input audio noise reduction.""" - To disable audio, set this to ["text"]. - """ + transcription: Optional[AudioTranscription] = None + """Configuration of the transcription model.""" turn_detection: Optional[RealtimeTranscriptionSessionTurnDetection] = None """Configuration for turn detection. @@ -39,3 +38,31 @@ class RealtimeTranscriptionSessionCreateResponse(BaseModel): the start and end of speech based on audio volume and respond at the end of user speech. """ + + +class Audio(BaseModel): + input: Optional[AudioInput] = None + + +class RealtimeTranscriptionSessionCreateResponse(BaseModel): + id: str + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" + + object: str + """The object type. Always `realtime.transcription_session`.""" + + type: Literal["transcription"] + """The type of session. Always `transcription` for transcription sessions.""" + + audio: Optional[Audio] = None + """Configuration for input audio for the session.""" + + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None + """Additional fields to include in server outputs. + + - `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ diff --git a/src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py b/src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py deleted file mode 100644 index 52254bed33..0000000000 --- a/src/openai/types/realtime/realtime_transcription_session_input_audio_transcription.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["RealtimeTranscriptionSessionInputAudioTranscription"] - - -class RealtimeTranscriptionSessionInputAudioTranscription(BaseModel): - language: Optional[str] = None - """The language of the input audio. - - Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - """ - - model: Optional[Literal["whisper-1", "gpt-4o-transcribe-latest", "gpt-4o-mini-transcribe", "gpt-4o-transcribe"]] = ( - None - ) - """The model to use for transcription. - - Current options are `whisper-1`, `gpt-4o-transcribe-latest`, - `gpt-4o-mini-transcribe`, and `gpt-4o-transcribe`. - """ - - prompt: Optional[str] = None - """ - An optional text to guide the model's style or continue a previous audio - segment. For `whisper-1`, the - [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". - """ diff --git a/src/openai/types/realtime/transcription_session_created.py b/src/openai/types/realtime/transcription_session_created.py deleted file mode 100644 index c358c5e8b0..0000000000 --- a/src/openai/types/realtime/transcription_session_created.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .realtime_transcription_session_create_response import RealtimeTranscriptionSessionCreateResponse - -__all__ = ["TranscriptionSessionCreated"] - - -class TranscriptionSessionCreated(BaseModel): - event_id: str - """The unique ID of the server event.""" - - session: RealtimeTranscriptionSessionCreateResponse - """A new Realtime transcription session configuration. - - When a session is created on the server via REST API, the session object also - contains an ephemeral key. Default TTL for keys is 10 minutes. This property is - not present when a session is updated via the WebSocket API. - """ - - type: Literal["transcription_session.created"] - """The event type, must be `transcription_session.created`.""" diff --git a/src/openai/types/realtime/transcription_session_update.py b/src/openai/types/realtime/transcription_session_update.py deleted file mode 100644 index 0faff9cb57..0000000000 --- a/src/openai/types/realtime/transcription_session_update.py +++ /dev/null @@ -1,98 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .audio_transcription import AudioTranscription -from .noise_reduction_type import NoiseReductionType - -__all__ = ["TranscriptionSessionUpdate", "Session", "SessionInputAudioNoiseReduction", "SessionTurnDetection"] - - -class SessionInputAudioNoiseReduction(BaseModel): - type: Optional[NoiseReductionType] = None - """Type of noise reduction. - - `near_field` is for close-talking microphones such as headphones, `far_field` is - for far-field microphones such as laptop or conference room microphones. - """ - - -class SessionTurnDetection(BaseModel): - prefix_padding_ms: Optional[int] = None - """Amount of audio to include before the VAD detected speech (in milliseconds). - - Defaults to 300ms. - """ - - silence_duration_ms: Optional[int] = None - """Duration of silence to detect speech stop (in milliseconds). - - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. - """ - - threshold: Optional[float] = None - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. - - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. - """ - - type: Optional[Literal["server_vad"]] = None - """Type of turn detection. - - Only `server_vad` is currently supported for transcription sessions. - """ - - -class Session(BaseModel): - include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None - """The set of items to include in the transcription. - - Current available items are: `item.input_audio_transcription.logprobs` - """ - - input_audio_format: Optional[Literal["pcm16", "g711_ulaw", "g711_alaw"]] = None - """The format of input audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must - be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian - byte order. - """ - - input_audio_noise_reduction: Optional[SessionInputAudioNoiseReduction] = None - """Configuration for input audio noise reduction. - - This can be set to `null` to turn off. Noise reduction filters audio added to - the input audio buffer before it is sent to VAD and the model. Filtering the - audio can improve VAD and turn detection accuracy (reducing false positives) and - model performance by improving perception of the input audio. - """ - - input_audio_transcription: Optional[AudioTranscription] = None - """Configuration for input audio transcription. - - The client can optionally set the language and prompt for transcription, these - offer additional guidance to the transcription service. - """ - - turn_detection: Optional[SessionTurnDetection] = None - """Configuration for turn detection. - - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. - """ - - -class TranscriptionSessionUpdate(BaseModel): - session: Session - """Realtime transcription session object configuration.""" - - type: Literal["transcription_session.update"] - """The event type, must be `transcription_session.update`.""" - - event_id: Optional[str] = None - """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/transcription_session_update_param.py b/src/openai/types/realtime/transcription_session_update_param.py deleted file mode 100644 index 55c67798b6..0000000000 --- a/src/openai/types/realtime/transcription_session_update_param.py +++ /dev/null @@ -1,99 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Literal, Required, TypedDict - -from .noise_reduction_type import NoiseReductionType -from .audio_transcription_param import AudioTranscriptionParam - -__all__ = ["TranscriptionSessionUpdateParam", "Session", "SessionInputAudioNoiseReduction", "SessionTurnDetection"] - - -class SessionInputAudioNoiseReduction(TypedDict, total=False): - type: NoiseReductionType - """Type of noise reduction. - - `near_field` is for close-talking microphones such as headphones, `far_field` is - for far-field microphones such as laptop or conference room microphones. - """ - - -class SessionTurnDetection(TypedDict, total=False): - prefix_padding_ms: int - """Amount of audio to include before the VAD detected speech (in milliseconds). - - Defaults to 300ms. - """ - - silence_duration_ms: int - """Duration of silence to detect speech stop (in milliseconds). - - Defaults to 500ms. With shorter values the model will respond more quickly, but - may jump in on short pauses from the user. - """ - - threshold: float - """Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. - - A higher threshold will require louder audio to activate the model, and thus - might perform better in noisy environments. - """ - - type: Literal["server_vad"] - """Type of turn detection. - - Only `server_vad` is currently supported for transcription sessions. - """ - - -class Session(TypedDict, total=False): - include: List[Literal["item.input_audio_transcription.logprobs"]] - """The set of items to include in the transcription. - - Current available items are: `item.input_audio_transcription.logprobs` - """ - - input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] - """The format of input audio. - - Options are `pcm16`, `g711_ulaw`, or `g711_alaw`. For `pcm16`, input audio must - be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian - byte order. - """ - - input_audio_noise_reduction: SessionInputAudioNoiseReduction - """Configuration for input audio noise reduction. - - This can be set to `null` to turn off. Noise reduction filters audio added to - the input audio buffer before it is sent to VAD and the model. Filtering the - audio can improve VAD and turn detection accuracy (reducing false positives) and - model performance by improving perception of the input audio. - """ - - input_audio_transcription: AudioTranscriptionParam - """Configuration for input audio transcription. - - The client can optionally set the language and prompt for transcription, these - offer additional guidance to the transcription service. - """ - - turn_detection: SessionTurnDetection - """Configuration for turn detection. - - Can be set to `null` to turn off. Server VAD means that the model will detect - the start and end of speech based on audio volume and respond at the end of user - speech. - """ - - -class TranscriptionSessionUpdateParam(TypedDict, total=False): - session: Required[Session] - """Realtime transcription session object configuration.""" - - type: Required[Literal["transcription_session.update"]] - """The event type, must be `transcription_session.update`.""" - - event_id: str - """Optional client-generated ID used to identify this event.""" diff --git a/src/openai/types/realtime/transcription_session_updated_event.py b/src/openai/types/realtime/transcription_session_updated_event.py deleted file mode 100644 index f6a52a12f3..0000000000 --- a/src/openai/types/realtime/transcription_session_updated_event.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .realtime_transcription_session_create_response import RealtimeTranscriptionSessionCreateResponse - -__all__ = ["TranscriptionSessionUpdatedEvent"] - - -class TranscriptionSessionUpdatedEvent(BaseModel): - event_id: str - """The unique ID of the server event.""" - - session: RealtimeTranscriptionSessionCreateResponse - """A new Realtime transcription session configuration. - - When a session is created on the server via REST API, the session object also - contains an ephemeral key. Default TTL for keys is 10 minutes. This property is - not present when a session is updated via the WebSocket API. - """ - - type: Literal["transcription_session.updated"] - """The event type, must be `transcription_session.updated`.""" From 4756247cee3d9548397b26a29109e76cc9522379 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:51:27 -0400 Subject: [PATCH 105/408] release: 1.107.2 (#2624) * chore(api): Minor docs and type updates for realtime * codegen metadata * chore(tests): simplify `get_platform` test `nest_asyncio` is archived and broken on some platforms so it's not worth keeping in our test suite. * release: 1.107.2 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 4 +- CHANGELOG.md | 9 +++ pyproject.toml | 2 +- requirements-dev.lock | 3 +- src/openai/_version.py | 2 +- src/openai/resources/responses/responses.py | 48 ++++++------ .../input_audio_buffer_timeout_triggered.py | 10 ++- .../realtime/realtime_audio_config_input.py | 7 +- .../realtime_audio_config_input_param.py | 10 ++- .../realtime_audio_input_turn_detection.py | 68 ++++++++++++----- ...altime_audio_input_turn_detection_param.py | 65 +++++++++++----- .../realtime_session_create_response.py | 74 ++++++++++++++----- ...ltime_transcription_session_audio_input.py | 7 +- ...transcription_session_audio_input_param.py | 10 ++- ...tion_session_audio_input_turn_detection.py | 67 +++++++++++++---- ...ession_audio_input_turn_detection_param.py | 64 ++++++++++++---- src/openai/types/responses/response.py | 8 +- .../types/responses/response_create_params.py | 8 +- .../realtime/test_client_secrets.py | 10 +-- tests/test_client.py | 53 ++----------- 21 files changed, 344 insertions(+), 187 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 25880b2e7b..32e0d8892c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.107.1" + ".": "1.107.2" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2aa16be875..e389718967 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-16cb18bed32bae8c5840fb39a1bf664026cc40463ad0c487dcb0df1bd3d72db0.yml -openapi_spec_hash: 4cb51b22f98dee1a90bc7add82d1d132 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-94b1e3cb0bdc616ff0c2f267c33dadd95f133b1f64e647aab6c64afb292b2793.yml +openapi_spec_hash: 2395319ac9befd59b6536ae7f9564a05 config_hash: 930dac3aa861344867e4ac84f037b5df diff --git a/CHANGELOG.md b/CHANGELOG.md index 19eab7da7e..31ccac5195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.107.2 (2025-09-12) + +Full Changelog: [v1.107.1...v1.107.2](https://github.com/openai/openai-python/compare/v1.107.1...v1.107.2) + +### Chores + +* **api:** Minor docs and type updates for realtime ([ab6a10d](https://github.com/openai/openai-python/commit/ab6a10da4ed7e6386695b6f5f29149d4870f85c9)) +* **tests:** simplify `get_platform` test ([01f03e0](https://github.com/openai/openai-python/commit/01f03e0ad1f9ab3f2ed8b7c13d652263c6d06378)) + ## 1.107.1 (2025-09-10) Full Changelog: [v1.107.0...v1.107.1](https://github.com/openai/openai-python/compare/v1.107.0...v1.107.1) diff --git a/pyproject.toml b/pyproject.toml index 326dc5a004..7cb1ef4f76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.107.1" +version = "1.107.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/requirements-dev.lock b/requirements-dev.lock index 7d690683e9..eaf136f7e6 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -70,7 +70,7 @@ filelock==3.12.4 frozenlist==1.7.0 # via aiohttp # via aiosignal -griffe==1.14.0 +griffe==1.13.0 h11==0.16.0 # via httpcore httpcore==1.0.9 @@ -108,7 +108,6 @@ multidict==6.5.0 mypy==1.14.1 mypy-extensions==1.0.0 # via mypy -nest-asyncio==1.6.0 nodeenv==1.8.0 # via pyright nox==2023.4.22 diff --git a/src/openai/_version.py b/src/openai/_version.py index f337b21cd5..70f9958885 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.107.1" # x-release-please-version +__version__ = "1.107.2" # x-release-please-version diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 837d2b2211..8acdb10b51 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -288,10 +288,10 @@ def create( truncation: The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use @@ -527,10 +527,10 @@ def create( truncation: The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use @@ -766,10 +766,10 @@ def create( truncation: The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use @@ -1719,10 +1719,10 @@ async def create( truncation: The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use @@ -1958,10 +1958,10 @@ async def create( truncation: The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use @@ -2197,10 +2197,10 @@ async def create( truncation: The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use diff --git a/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py index ed592ac06b..5c5dc5cfa6 100644 --- a/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py +++ b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py @@ -9,10 +9,16 @@ class InputAudioBufferTimeoutTriggered(BaseModel): audio_end_ms: int - """Millisecond offset where speech ended within the buffered audio.""" + """ + Millisecond offset of audio written to the input audio buffer at the time the + timeout was triggered. + """ audio_start_ms: int - """Millisecond offset where speech started within the buffered audio.""" + """ + Millisecond offset of audio written to the input audio buffer that was after the + playback time of the last model response. + """ event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/realtime_audio_config_input.py b/src/openai/types/realtime/realtime_audio_config_input.py index fd96e2a52d..cfcb7f22d4 100644 --- a/src/openai/types/realtime/realtime_audio_config_input.py +++ b/src/openai/types/realtime/realtime_audio_config_input.py @@ -49,8 +49,11 @@ class RealtimeAudioConfigInput(BaseModel): """Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio diff --git a/src/openai/types/realtime/realtime_audio_config_input_param.py b/src/openai/types/realtime/realtime_audio_config_input_param.py index 1dfb439006..730f46cfec 100644 --- a/src/openai/types/realtime/realtime_audio_config_input_param.py +++ b/src/openai/types/realtime/realtime_audio_config_input_param.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import TypedDict from .noise_reduction_type import NoiseReductionType @@ -46,12 +47,15 @@ class RealtimeAudioConfigInputParam(TypedDict, total=False): transcription, these offer additional guidance to the transcription service. """ - turn_detection: RealtimeAudioInputTurnDetectionParam + turn_detection: Optional[RealtimeAudioInputTurnDetectionParam] """Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_audio_input_turn_detection.py index 1c736ab2b7..d3f4e00316 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection.py @@ -1,33 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel -__all__ = ["RealtimeAudioInputTurnDetection"] +__all__ = ["RealtimeAudioInputTurnDetection", "ServerVad", "SemanticVad"] -class RealtimeAudioInputTurnDetection(BaseModel): +class ServerVad(BaseModel): + type: Literal["server_vad"] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + create_response: Optional[bool] = None """ Whether or not to automatically generate a response when a VAD stop event occurs. """ - eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None - """Used only for `semantic_vad` mode. + idle_timeout_ms: Optional[int] = None + """Optional timeout after which a model response will be triggered automatically. - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, - 4s, and 2s respectively. - """ + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. - idle_timeout_ms: Optional[int] = None - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received and emits a `timeout_triggered` event. + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. """ interrupt_response: Optional[bool] = None @@ -60,5 +65,34 @@ class RealtimeAudioInputTurnDetection(BaseModel): perform better in noisy environments. """ - type: Optional[Literal["server_vad", "semantic_vad"]] = None - """Type of turn detection.""" + +class SemanticVad(BaseModel): + type: Literal["semantic_vad"] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeAudioInputTurnDetection: TypeAlias = Annotated[ + Union[ServerVad, SemanticVad, None], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py index 79cabec708..09b8cfd159 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py @@ -2,32 +2,36 @@ from __future__ import annotations -from typing import Optional -from typing_extensions import Literal, TypedDict +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["RealtimeAudioInputTurnDetectionParam"] +__all__ = ["RealtimeAudioInputTurnDetectionParam", "ServerVad", "SemanticVad"] -class RealtimeAudioInputTurnDetectionParam(TypedDict, total=False): +class ServerVad(TypedDict, total=False): + type: Required[Literal["server_vad"]] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + create_response: bool """ Whether or not to automatically generate a response when a VAD stop event occurs. """ - eagerness: Literal["low", "medium", "high", "auto"] - """Used only for `semantic_vad` mode. + idle_timeout_ms: Optional[int] + """Optional timeout after which a model response will be triggered automatically. - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, - 4s, and 2s respectively. - """ + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. - idle_timeout_ms: Optional[int] - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received and emits a `timeout_triggered` event. + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. """ interrupt_response: bool @@ -60,5 +64,32 @@ class RealtimeAudioInputTurnDetectionParam(TypedDict, total=False): perform better in noisy environments. """ - type: Literal["server_vad", "semantic_vad"] - """Type of turn detection.""" + +class SemanticVad(TypedDict, total=False): + type: Required[Literal["semantic_vad"]] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeAudioInputTurnDetectionParam: TypeAlias = Union[ServerVad, SemanticVad] diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 7779f07a6e..8d7bfd6d8e 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -1,8 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Union, Optional -from typing_extensions import Literal, TypeAlias +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel from .audio_transcription import AudioTranscription from .realtime_truncation import RealtimeTruncation @@ -21,6 +22,8 @@ "AudioInput", "AudioInputNoiseReduction", "AudioInputTurnDetection", + "AudioInputTurnDetectionServerVad", + "AudioInputTurnDetectionSemanticVad", "AudioOutput", "ToolChoice", "Tool", @@ -45,26 +48,30 @@ class AudioInputNoiseReduction(BaseModel): """ -class AudioInputTurnDetection(BaseModel): +class AudioInputTurnDetectionServerVad(BaseModel): + type: Literal["server_vad"] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + create_response: Optional[bool] = None """ Whether or not to automatically generate a response when a VAD stop event occurs. """ - eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None - """Used only for `semantic_vad` mode. + idle_timeout_ms: Optional[int] = None + """Optional timeout after which a model response will be triggered automatically. - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, - 4s, and 2s respectively. - """ + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. - idle_timeout_ms: Optional[int] = None - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received and emits a `timeout_triggered` event. + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. """ interrupt_response: Optional[bool] = None @@ -97,8 +104,38 @@ class AudioInputTurnDetection(BaseModel): perform better in noisy environments. """ - type: Optional[Literal["server_vad", "semantic_vad"]] = None - """Type of turn detection.""" + +class AudioInputTurnDetectionSemanticVad(BaseModel): + type: Literal["semantic_vad"] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +AudioInputTurnDetection: TypeAlias = Annotated[ + Union[AudioInputTurnDetectionServerVad, AudioInputTurnDetectionSemanticVad, None], + PropertyInfo(discriminator="type"), +] class AudioInput(BaseModel): @@ -130,8 +167,11 @@ class AudioInput(BaseModel): """Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input.py b/src/openai/types/realtime/realtime_transcription_session_audio_input.py index 0ae92959aa..efc321cbeb 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input.py @@ -51,8 +51,11 @@ class RealtimeTranscriptionSessionAudioInput(BaseModel): """Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py index a8263789dc..c9153b68a4 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import TypedDict from .noise_reduction_type import NoiseReductionType @@ -48,12 +49,15 @@ class RealtimeTranscriptionSessionAudioInputParam(TypedDict, total=False): transcription, these offer additional guidance to the transcription service. """ - turn_detection: RealtimeTranscriptionSessionAudioInputTurnDetectionParam + turn_detection: Optional[RealtimeTranscriptionSessionAudioInputTurnDetectionParam] """Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually - trigger model response. Server VAD means that the model will detect the start - and end of speech based on audio volume and respond at the end of user speech. + trigger model response. + + Server VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + Semantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py index 0cac36f7a3..7dc7a8f302 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py @@ -1,32 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel -__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetection"] +__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetection", "ServerVad", "SemanticVad"] -class RealtimeTranscriptionSessionAudioInputTurnDetection(BaseModel): +class ServerVad(BaseModel): + type: Literal["server_vad"] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + create_response: Optional[bool] = None """ Whether or not to automatically generate a response when a VAD stop event occurs. """ - eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None - """Used only for `semantic_vad` mode. + idle_timeout_ms: Optional[int] = None + """Optional timeout after which a model response will be triggered automatically. - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. - """ + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. - idle_timeout_ms: Optional[int] = None - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. """ interrupt_response: Optional[bool] = None @@ -59,5 +65,34 @@ class RealtimeTranscriptionSessionAudioInputTurnDetection(BaseModel): perform better in noisy environments. """ - type: Optional[Literal["server_vad", "semantic_vad"]] = None - """Type of turn detection.""" + +class SemanticVad(BaseModel): + type: Literal["semantic_vad"] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: Optional[bool] = None + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Optional[Literal["low", "medium", "high", "auto"]] = None + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: Optional[bool] = None + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeTranscriptionSessionAudioInputTurnDetection: TypeAlias = Annotated[ + Union[ServerVad, SemanticVad, None], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py index e76dc9a8fe..d899b8c5c1 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py @@ -2,31 +2,36 @@ from __future__ import annotations -from typing import Optional -from typing_extensions import Literal, TypedDict +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetectionParam"] +__all__ = ["RealtimeTranscriptionSessionAudioInputTurnDetectionParam", "ServerVad", "SemanticVad"] -class RealtimeTranscriptionSessionAudioInputTurnDetectionParam(TypedDict, total=False): +class ServerVad(TypedDict, total=False): + type: Required[Literal["server_vad"]] + """Type of turn detection, `server_vad` to turn on simple Server VAD.""" + create_response: bool """ Whether or not to automatically generate a response when a VAD stop event occurs. """ - eagerness: Literal["low", "medium", "high", "auto"] - """Used only for `semantic_vad` mode. + idle_timeout_ms: Optional[int] + """Optional timeout after which a model response will be triggered automatically. - The eagerness of the model to respond. `low` will wait longer for the user to - continue speaking, `high` will respond more quickly. `auto` is the default and - is equivalent to `medium`. - """ + This is useful for situations in which a long pause from the user is unexpected, + such as a phone call. The model will effectively prompt the user to continue the + conversation based on the current context. - idle_timeout_ms: Optional[int] - """ - Optional idle timeout after which turn detection will auto-timeout when no - additional audio is received. + The timeout value will be applied after the last model response's audio has + finished playing, i.e. it's set to the `response.done` time plus audio playback + duration. + + An `input_audio_buffer.timeout_triggered` event (plus events associated with the + Response) will be emitted when the timeout is reached. Idle timeout is currently + only supported for `server_vad` mode. """ interrupt_response: bool @@ -59,5 +64,32 @@ class RealtimeTranscriptionSessionAudioInputTurnDetectionParam(TypedDict, total= perform better in noisy environments. """ - type: Literal["server_vad", "semantic_vad"] - """Type of turn detection.""" + +class SemanticVad(TypedDict, total=False): + type: Required[Literal["semantic_vad"]] + """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" + + create_response: bool + """ + Whether or not to automatically generate a response when a VAD stop event + occurs. + """ + + eagerness: Literal["low", "medium", "high", "auto"] + """Used only for `semantic_vad` mode. + + The eagerness of the model to respond. `low` will wait longer for the user to + continue speaking, `high` will respond more quickly. `auto` is the default and + is equivalent to `medium`. `low`, `medium`, and `high` have max timeouts of 8s, + 4s, and 2s respectively. + """ + + interrupt_response: bool + """ + Whether or not to automatically interrupt any ongoing response with output to + the default conversation (i.e. `conversation` of `auto`) when a VAD start event + occurs. + """ + + +RealtimeTranscriptionSessionAudioInputTurnDetectionParam: TypeAlias = Union[ServerVad, SemanticVad] diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 163648ef3e..423b6f20f1 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -252,10 +252,10 @@ class Response(BaseModel): truncation: Optional[Literal["auto", "disabled"]] = None """The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. """ diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index be687c0aff..af0d5e7483 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -252,10 +252,10 @@ class ResponseCreateParamsBase(TypedDict, total=False): truncation: Optional[Literal["auto", "disabled"]] """The truncation strategy to use for the model response. - - `auto`: If the context of this response and previous ones exceeds the model's - context window size, the model will truncate the response to fit the context - window by dropping input items in the middle of the conversation. - - `disabled` (default): If a model response will exceed the context window size + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size for a model, the request will fail with a 400 error. """ diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py index b7bb0e5aa7..cd15b4be52 100644 --- a/tests/api_resources/realtime/test_client_secrets.py +++ b/tests/api_resources/realtime/test_client_secrets.py @@ -44,14 +44,13 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "prompt": "prompt", }, "turn_detection": { + "type": "server_vad", "create_response": True, - "eagerness": "low", - "idle_timeout_ms": 0, + "idle_timeout_ms": 5000, "interrupt_response": True, "prefix_padding_ms": 0, "silence_duration_ms": 0, "threshold": 0, - "type": "server_vad", }, }, "output": { @@ -141,14 +140,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "prompt": "prompt", }, "turn_detection": { + "type": "server_vad", "create_response": True, - "eagerness": "low", - "idle_timeout_ms": 0, + "idle_timeout_ms": 5000, "interrupt_response": True, "prefix_padding_ms": 0, "silence_duration_ms": 0, "threshold": 0, - "type": "server_vad", }, }, "output": { diff --git a/tests/test_client.py b/tests/test_client.py index e5300e55d7..3287e0e706 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,13 +6,10 @@ import os import sys import json -import time import asyncio import inspect -import subprocess import tracemalloc from typing import Any, Union, Protocol, cast -from textwrap import dedent from unittest import mock from typing_extensions import Literal @@ -23,6 +20,7 @@ from openai import OpenAI, AsyncOpenAI, APIResponseValidationError from openai._types import Omit +from openai._utils import asyncify from openai._models import BaseModel, FinalRequestOptions from openai._streaming import Stream, AsyncStream from openai._exceptions import OpenAIError, APIStatusError, APITimeoutError, APIResponseValidationError @@ -30,8 +28,10 @@ DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, + OtherPlatform, DefaultHttpxClient, DefaultAsyncHttpxClient, + get_platform, make_request_options, ) @@ -1857,50 +1857,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success - def test_get_platform(self) -> None: - # A previous implementation of asyncify could leave threads unterminated when - # used with nest_asyncio. - # - # Since nest_asyncio.apply() is global and cannot be un-applied, this - # test is run in a separate process to avoid affecting other tests. - test_code = dedent(""" - import asyncio - import nest_asyncio - import threading - - from openai._utils import asyncify - from openai._base_client import get_platform - - async def test_main() -> None: - result = await asyncify(get_platform)() - print(result) - for thread in threading.enumerate(): - print(thread.name) - - nest_asyncio.apply() - asyncio.run(test_main()) - """) - with subprocess.Popen( - [sys.executable, "-c", test_code], - text=True, - ) as process: - timeout = 10 # seconds - - start_time = time.monotonic() - while True: - return_code = process.poll() - if return_code is not None: - if return_code != 0: - raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") - - # success - break - - if time.monotonic() - start_time > timeout: - process.kill() - raise AssertionError("calling get_platform using asyncify resulted in a hung process") - - time.sleep(0.1) + async def test_get_platform(self) -> None: + platform = await asyncify(get_platform)() + assert isinstance(platform, (str, OtherPlatform)) async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly From 514de0fe148bc44bed09491b97eeec44d8071c81 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 19:52:16 +0000 Subject: [PATCH 106/408] chore(api): docs and spec refactoring --- .stats.yml | 6 +++--- .../resources/chat/completions/completions.py | 16 ++++++++++------ .../resources/conversations/conversations.py | 16 ++++++++++++---- .../types/audio/transcription_create_params.py | 12 ++++++------ .../chat_completion_assistant_message_param.py | 4 ++-- src/openai/types/chat/completion_list_params.py | 8 ++++++-- .../conversations/conversation_create_params.py | 7 +++++-- src/openai/types/evals/run_cancel_response.py | 9 ++++++--- src/openai/types/evals/run_create_params.py | 9 ++++++--- src/openai/types/evals/run_create_response.py | 9 ++++++--- src/openai/types/evals/run_list_response.py | 9 ++++++--- src/openai/types/evals/run_retrieve_response.py | 9 ++++++--- .../realtime/realtime_response_create_params.py | 4 ++-- .../realtime_response_create_params_param.py | 4 ++-- .../realtime/realtime_session_create_request.py | 4 ++-- .../realtime_session_create_request_param.py | 4 ++-- .../realtime/realtime_session_create_response.py | 4 ++-- src/openai/types/responses/response.py | 4 ++-- .../response_code_interpreter_tool_call.py | 6 +++--- .../response_code_interpreter_tool_call_param.py | 6 +++--- .../types/responses/response_create_params.py | 4 ++-- 21 files changed, 94 insertions(+), 60 deletions(-) diff --git a/.stats.yml b/.stats.yml index e389718967..905a02c44a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-94b1e3cb0bdc616ff0c2f267c33dadd95f133b1f64e647aab6c64afb292b2793.yml -openapi_spec_hash: 2395319ac9befd59b6536ae7f9564a05 -config_hash: 930dac3aa861344867e4ac84f037b5df +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d30ff992a48873c1466c49f3c01f2ec8933faebff23424748f8d056065b1bcef.yml +openapi_spec_hash: e933ec43b46f45c348adb78840e5808d +config_hash: bf45940f0a7805b4ec2017eecdd36893 diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 168cf04dbc..f29792a207 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -1300,10 +1300,12 @@ def list( limit: Number of Chat Completions to retrieve. - metadata: - A list of metadata keys to filter the Chat Completions by. Example: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. - `metadata[key1]=value1&metadata[key2]=value2` + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The model used to generate the Chat Completions. @@ -2736,10 +2738,12 @@ def list( limit: Number of Chat Completions to retrieve. - metadata: - A list of metadata keys to filter the Chat Completions by. Example: + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. - `metadata[key1]=value1&metadata[key2]=value2` + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. model: The model used to generate the Chat Completions. diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py index 802620e6ad..c0239d402c 100644 --- a/src/openai/resources/conversations/conversations.py +++ b/src/openai/resources/conversations/conversations.py @@ -73,8 +73,12 @@ def create( items: Initial items to include in the conversation context. You may add up to 20 items at a time. - metadata: Set of 16 key-value pairs that can be attached to an object. Useful for storing - additional information about the object in a structured format. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -250,8 +254,12 @@ async def create( items: Initial items to include in the conversation context. You may add up to 20 items at a time. - metadata: Set of 16 key-value pairs that can be attached to an object. Useful for storing - additional information about the object in a structured format. + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index 8271b054ab..f7abcced87 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -43,12 +43,12 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): """ include: List[TranscriptionInclude] - """Additional information to include in the transcription response. - - `logprobs` will return the log probabilities of the tokens in the response to - understand the model's confidence in the transcription. `logprobs` only works - with response_format set to `json` and only with the models `gpt-4o-transcribe` - and `gpt-4o-mini-transcribe`. + """ + Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`. """ language: str diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 212d933e9b..1a08a959db 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -38,8 +38,8 @@ class ChatCompletionAssistantMessageParam(TypedDict, total=False): """The role of the messages author, in this case `assistant`.""" audio: Optional[Audio] - """Data about a previous audio response from the model. - + """ + Data about a previous audio response from the model. [Learn more](https://platform.openai.com/docs/guides/audio). """ diff --git a/src/openai/types/chat/completion_list_params.py b/src/openai/types/chat/completion_list_params.py index d93da834a3..32bd3f5c0a 100644 --- a/src/openai/types/chat/completion_list_params.py +++ b/src/openai/types/chat/completion_list_params.py @@ -18,9 +18,13 @@ class CompletionListParams(TypedDict, total=False): """Number of Chat Completions to retrieve.""" metadata: Optional[Metadata] - """A list of metadata keys to filter the Chat Completions by. Example: + """Set of 16 key-value pairs that can be attached to an object. - `metadata[key1]=value1&metadata[key2]=value2` + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ model: str diff --git a/src/openai/types/conversations/conversation_create_params.py b/src/openai/types/conversations/conversation_create_params.py index 7ad3f8ae2d..0d84f503bd 100644 --- a/src/openai/types/conversations/conversation_create_params.py +++ b/src/openai/types/conversations/conversation_create_params.py @@ -21,6 +21,9 @@ class ConversationCreateParams(TypedDict, total=False): metadata: Optional[Metadata] """Set of 16 key-value pairs that can be attached to an object. - Useful for storing additional information about the object in a structured - format. + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 44f9cfc453..8f43494e68 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -100,9 +100,12 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """Optional reasoning effort parameter. - - This is a query parameter used to select responses. + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ temperature: Optional[float] = None diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index ef9541ff0a..35813c8901 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -113,9 +113,12 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total """ reasoning_effort: Optional[ReasoningEffort] - """Optional reasoning effort parameter. - - This is a query parameter used to select responses. + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ temperature: Optional[float] diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 70641d6db8..c842a5ad2f 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -100,9 +100,12 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """Optional reasoning effort parameter. - - This is a query parameter used to select responses. + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ temperature: Optional[float] = None diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index e31d570a84..5a5c2efbb3 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -100,9 +100,12 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """Optional reasoning effort parameter. - - This is a query parameter used to select responses. + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ temperature: Optional[float] = None diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index 62213d3edd..f341296875 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -100,9 +100,12 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """Optional reasoning effort parameter. - - This is a query parameter used to select responses. + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. """ temperature: Optional[float] = None diff --git a/src/openai/types/realtime/realtime_response_create_params.py b/src/openai/types/realtime/realtime_response_create_params.py index 4dfd1fd386..e8486220bf 100644 --- a/src/openai/types/realtime/realtime_response_create_params.py +++ b/src/openai/types/realtime/realtime_response_create_params.py @@ -83,8 +83,8 @@ class RealtimeResponseCreateParams(BaseModel): """ prompt: Optional[ResponsePrompt] = None - """Reference to a prompt template and its variables. - + """ + Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ diff --git a/src/openai/types/realtime/realtime_response_create_params_param.py b/src/openai/types/realtime/realtime_response_create_params_param.py index eceffcccb7..116384bd82 100644 --- a/src/openai/types/realtime/realtime_response_create_params_param.py +++ b/src/openai/types/realtime/realtime_response_create_params_param.py @@ -84,8 +84,8 @@ class RealtimeResponseCreateParamsParam(TypedDict, total=False): """ prompt: Optional[ResponsePromptParam] - """Reference to a prompt template and its variables. - + """ + Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 578bc43821..755dbe8638 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -76,8 +76,8 @@ class RealtimeSessionCreateRequest(BaseModel): """ prompt: Optional[ResponsePrompt] = None - """Reference to a prompt template and its variables. - + """ + Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index 5f7819fa61..cd4ef71ba2 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -76,8 +76,8 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): """ prompt: Optional[ResponsePromptParam] - """Reference to a prompt template and its variables. - + """ + Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 8d7bfd6d8e..2d6912d072 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -429,8 +429,8 @@ class RealtimeSessionCreateResponse(BaseModel): """ prompt: Optional[ResponsePrompt] = None - """Reference to a prompt template and its variables. - + """ + Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 423b6f20f1..a1133a41f5 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -180,8 +180,8 @@ class Response(BaseModel): """ prompt: Optional[ResponsePrompt] = None - """Reference to a prompt template and its variables. - + """ + Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ diff --git a/src/openai/types/responses/response_code_interpreter_tool_call.py b/src/openai/types/responses/response_code_interpreter_tool_call.py index 257937118b..ed720ecd42 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call.py @@ -39,9 +39,9 @@ class ResponseCodeInterpreterToolCall(BaseModel): """The ID of the container used to run the code.""" outputs: Optional[List[Output]] = None - """The outputs generated by the code interpreter, such as logs or images. - - Can be null if no outputs are available. + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. """ status: Literal["in_progress", "completed", "incomplete", "interpreting", "failed"] diff --git a/src/openai/types/responses/response_code_interpreter_tool_call_param.py b/src/openai/types/responses/response_code_interpreter_tool_call_param.py index 435091001f..78b90ca87e 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call_param.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call_param.py @@ -38,9 +38,9 @@ class ResponseCodeInterpreterToolCallParam(TypedDict, total=False): """The ID of the container used to run the code.""" outputs: Required[Optional[Iterable[Output]]] - """The outputs generated by the code interpreter, such as logs or images. - - Can be null if no outputs are available. + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. """ status: Required[Literal["in_progress", "completed", "incomplete", "interpreting", "failed"]] diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index af0d5e7483..ba5c45ffee 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -134,8 +134,8 @@ class ResponseCreateParamsBase(TypedDict, total=False): """ prompt: Optional[ResponsePromptParam] - """Reference to a prompt template and its variables. - + """ + Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ From 0b4bc5049f31f9d03b773d6919064e007b378778 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 19:52:49 +0000 Subject: [PATCH 107/408] release: 1.107.3 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 32e0d8892c..3b81c9b87e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.107.2" + ".": "1.107.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ccac5195..b9314bd48a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.107.3 (2025-09-15) + +Full Changelog: [v1.107.2...v1.107.3](https://github.com/openai/openai-python/compare/v1.107.2...v1.107.3) + +### Chores + +* **api:** docs and spec refactoring ([9bab5da](https://github.com/openai/openai-python/commit/9bab5da1802c3575c58e73ed1470dd5fa61fd1d2)) +* **tests:** simplify `get_platform` test ([0b1f6a2](https://github.com/openai/openai-python/commit/0b1f6a28d5a59e10873264e976d2e332903eef29)) + ## 1.107.2 (2025-09-12) Full Changelog: [v1.107.1...v1.107.2](https://github.com/openai/openai-python/compare/v1.107.1...v1.107.2) diff --git a/pyproject.toml b/pyproject.toml index 7cb1ef4f76..190542e6dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.107.2" +version = "1.107.3" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 70f9958885..aa7660d137 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.107.2" # x-release-please-version +__version__ = "1.107.3" # x-release-please-version From 0d85ca08c83a408abf3f03b46189e6bf39f68ac6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 18:02:28 -0400 Subject: [PATCH 108/408] release: 1.108.0 (#2635) * chore(internal): update pydantic dependency * feat(api): type updates for conversations, reasoning_effort and results for evals * release: 1.108.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +-- CHANGELOG.md | 13 +++++++ api.md | 15 +++----- pyproject.toml | 2 +- requirements-dev.lock | 10 +++-- requirements.lock | 11 ++++-- src/openai/_models.py | 14 +++++-- src/openai/_version.py | 2 +- src/openai/types/conversations/__init__.py | 10 ++--- .../container_file_citation_body.py | 27 -------------- .../types/conversations/file_citation_body.py | 21 ----------- .../types/conversations/input_file_content.py | 19 +--------- .../conversations/input_file_content_param.py | 7 ++++ .../conversations/input_image_content.py | 25 +------------ .../input_image_content_param.py | 7 ++++ .../types/conversations/input_text_content.py | 12 +----- .../conversations/input_text_content_param.py | 7 ++++ src/openai/types/conversations/lob_prob.py | 18 --------- src/openai/types/conversations/message.py | 20 +++++----- .../conversations/output_text_content.py | 29 ++------------- .../output_text_content_param.py | 7 ++++ .../types/conversations/refusal_content.py | 12 +----- .../conversations/refusal_content_param.py | 7 ++++ .../types/conversations/top_log_prob.py | 15 -------- .../types/conversations/url_citation_body.py | 24 ------------ ...create_eval_completions_run_data_source.py | 10 +++++ ..._eval_completions_run_data_source_param.py | 10 +++++ src/openai/types/evals/run_cancel_response.py | 9 +++++ src/openai/types/evals/run_create_params.py | 9 +++++ src/openai/types/evals/run_create_response.py | 9 +++++ src/openai/types/evals/run_list_response.py | 9 +++++ .../types/evals/run_retrieve_response.py | 9 +++++ .../evals/runs/output_item_list_response.py | 35 +++++++++++++++--- .../runs/output_item_retrieve_response.py | 35 +++++++++++++++--- .../types/graders/score_model_grader.py | 35 +++++++++++++++++- .../types/graders/score_model_grader_param.py | 37 +++++++++++++++++-- 37 files changed, 301 insertions(+), 248 deletions(-) delete mode 100644 src/openai/types/conversations/container_file_citation_body.py delete mode 100644 src/openai/types/conversations/file_citation_body.py create mode 100644 src/openai/types/conversations/input_file_content_param.py create mode 100644 src/openai/types/conversations/input_image_content_param.py create mode 100644 src/openai/types/conversations/input_text_content_param.py delete mode 100644 src/openai/types/conversations/lob_prob.py create mode 100644 src/openai/types/conversations/output_text_content_param.py create mode 100644 src/openai/types/conversations/refusal_content_param.py delete mode 100644 src/openai/types/conversations/top_log_prob.py delete mode 100644 src/openai/types/conversations/url_citation_body.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3b81c9b87e..102fa47016 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.107.3" + ".": "1.108.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 905a02c44a..2dd0aef46a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d30ff992a48873c1466c49f3c01f2ec8933faebff23424748f8d056065b1bcef.yml -openapi_spec_hash: e933ec43b46f45c348adb78840e5808d -config_hash: bf45940f0a7805b4ec2017eecdd36893 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-380330a93b5d010391ca3b36ea193c5353b0dfdf2ddd02789ef84a84ce427e82.yml +openapi_spec_hash: 859703234259ecdd2a3c6f4de88eb504 +config_hash: b619b45c1e7facf819f902dee8fa4f97 diff --git a/CHANGELOG.md b/CHANGELOG.md index b9314bd48a..1e35189611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.108.0 (2025-09-17) + +Full Changelog: [v1.107.3...v1.108.0](https://github.com/openai/openai-python/compare/v1.107.3...v1.108.0) + +### Features + +* **api:** type updates for conversations, reasoning_effort and results for evals ([c2ee28c](https://github.com/openai/openai-python/commit/c2ee28c1b77eed98766fbb01cf1ad2ee240f412e)) + + +### Chores + +* **internal:** update pydantic dependency ([369d10a](https://github.com/openai/openai-python/commit/369d10a40dfe744f6bfc10c99eb1f58176500120)) + ## 1.107.3 (2025-09-15) Full Changelog: [v1.107.2...v1.107.3](https://github.com/openai/openai-python/compare/v1.107.2...v1.107.3) diff --git a/api.md b/api.md index 73b8427387..6bbb47f78c 100644 --- a/api.md +++ b/api.md @@ -991,22 +991,17 @@ Types: ```python from openai.types.conversations import ( ComputerScreenshotContent, - ContainerFileCitationBody, Conversation, ConversationDeleted, ConversationDeletedResource, - FileCitationBody, - InputFileContent, - InputImageContent, - InputTextContent, - LobProb, Message, - OutputTextContent, - RefusalContent, SummaryTextContent, TextContent, - TopLogProb, - URLCitationBody, + InputTextContent, + OutputTextContent, + RefusalContent, + InputImageContent, + InputFileContent, ) ``` diff --git a/pyproject.toml b/pyproject.toml index 190542e6dc..058b7cda6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.107.3" +version = "1.108.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/requirements-dev.lock b/requirements-dev.lock index eaf136f7e6..0bd1c2c70f 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -108,6 +108,7 @@ multidict==6.5.0 mypy==1.14.1 mypy-extensions==1.0.0 # via mypy +nest-asyncio==1.6.0 nodeenv==1.8.0 # via pyright nox==2023.4.22 @@ -133,11 +134,11 @@ portalocker==2.10.1 propcache==0.3.2 # via aiohttp # via yarl -pycparser==2.22 +pycparser==2.23 # via cffi -pydantic==2.10.3 +pydantic==2.11.9 # via openai -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic pygments==2.18.0 # via pytest @@ -199,6 +200,9 @@ typing-extensions==4.12.2 # via pydantic # via pydantic-core # via pyright + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic tzdata==2024.1 # via pandas urllib3==2.2.1 diff --git a/requirements.lock b/requirements.lock index 3b6ece87e2..a2b6845942 100644 --- a/requirements.lock +++ b/requirements.lock @@ -67,11 +67,11 @@ pandas-stubs==2.2.2.240807 propcache==0.3.2 # via aiohttp # via yarl -pycparser==2.22 +pycparser==2.23 # via cffi -pydantic==2.10.3 +pydantic==2.11.9 # via openai -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic python-dateutil==2.9.0.post0 # via pandas @@ -93,7 +93,10 @@ typing-extensions==4.12.2 # via openai # via pydantic # via pydantic-core -tzdata==2024.1 + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic +tzdata==2025.2 # via pandas websockets==15.0.1 # via openai diff --git a/src/openai/_models.py b/src/openai/_models.py index 8ee8612d1e..af71a91850 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -281,7 +281,7 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, @@ -289,6 +289,7 @@ def model_dump( warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, serialize_as_any: bool = False, + fallback: Callable[[Any], Any] | None = None, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -320,10 +321,12 @@ def model_dump( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -338,13 +341,14 @@ def model_dump_json( indent: int | None = None, include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, + fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -373,11 +377,13 @@ def model_dump_json( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, diff --git a/src/openai/_version.py b/src/openai/_version.py index aa7660d137..7030fe068c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.107.3" # x-release-please-version +__version__ = "1.108.0" # x-release-please-version diff --git a/src/openai/types/conversations/__init__.py b/src/openai/types/conversations/__init__.py index 538966db4f..9dec848737 100644 --- a/src/openai/types/conversations/__init__.py +++ b/src/openai/types/conversations/__init__.py @@ -3,15 +3,11 @@ from __future__ import annotations from .message import Message as Message -from .lob_prob import LobProb as LobProb from .conversation import Conversation as Conversation from .text_content import TextContent as TextContent -from .top_log_prob import TopLogProb as TopLogProb from .refusal_content import RefusalContent as RefusalContent from .item_list_params import ItemListParams as ItemListParams from .conversation_item import ConversationItem as ConversationItem -from .url_citation_body import URLCitationBody as URLCitationBody -from .file_citation_body import FileCitationBody as FileCitationBody from .input_file_content import InputFileContent as InputFileContent from .input_text_content import InputTextContent as InputTextContent from .item_create_params import ItemCreateParams as ItemCreateParams @@ -19,9 +15,13 @@ from .output_text_content import OutputTextContent as OutputTextContent from .item_retrieve_params import ItemRetrieveParams as ItemRetrieveParams from .summary_text_content import SummaryTextContent as SummaryTextContent +from .refusal_content_param import RefusalContentParam as RefusalContentParam from .conversation_item_list import ConversationItemList as ConversationItemList +from .input_file_content_param import InputFileContentParam as InputFileContentParam +from .input_text_content_param import InputTextContentParam as InputTextContentParam +from .input_image_content_param import InputImageContentParam as InputImageContentParam +from .output_text_content_param import OutputTextContentParam as OutputTextContentParam from .conversation_create_params import ConversationCreateParams as ConversationCreateParams from .conversation_update_params import ConversationUpdateParams as ConversationUpdateParams from .computer_screenshot_content import ComputerScreenshotContent as ComputerScreenshotContent -from .container_file_citation_body import ContainerFileCitationBody as ContainerFileCitationBody from .conversation_deleted_resource import ConversationDeletedResource as ConversationDeletedResource diff --git a/src/openai/types/conversations/container_file_citation_body.py b/src/openai/types/conversations/container_file_citation_body.py deleted file mode 100644 index ea460df2e2..0000000000 --- a/src/openai/types/conversations/container_file_citation_body.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ContainerFileCitationBody"] - - -class ContainerFileCitationBody(BaseModel): - container_id: str - """The ID of the container file.""" - - end_index: int - """The index of the last character of the container file citation in the message.""" - - file_id: str - """The ID of the file.""" - - filename: str - """The filename of the container file cited.""" - - start_index: int - """The index of the first character of the container file citation in the message.""" - - type: Literal["container_file_citation"] - """The type of the container file citation. Always `container_file_citation`.""" diff --git a/src/openai/types/conversations/file_citation_body.py b/src/openai/types/conversations/file_citation_body.py deleted file mode 100644 index ea90ae381d..0000000000 --- a/src/openai/types/conversations/file_citation_body.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["FileCitationBody"] - - -class FileCitationBody(BaseModel): - file_id: str - """The ID of the file.""" - - filename: str - """The filename of the file cited.""" - - index: int - """The index of the file in the list of files.""" - - type: Literal["file_citation"] - """The type of the file citation. Always `file_citation`.""" diff --git a/src/openai/types/conversations/input_file_content.py b/src/openai/types/conversations/input_file_content.py index 6aef7a89d9..ca555d85fc 100644 --- a/src/openai/types/conversations/input_file_content.py +++ b/src/openai/types/conversations/input_file_content.py @@ -1,22 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel +from ..responses.response_input_file import ResponseInputFile __all__ = ["InputFileContent"] - -class InputFileContent(BaseModel): - file_id: Optional[str] = None - """The ID of the file to be sent to the model.""" - - type: Literal["input_file"] - """The type of the input item. Always `input_file`.""" - - file_url: Optional[str] = None - """The URL of the file to be sent to the model.""" - - filename: Optional[str] = None - """The name of the file to be sent to the model.""" +InputFileContent = ResponseInputFile diff --git a/src/openai/types/conversations/input_file_content_param.py b/src/openai/types/conversations/input_file_content_param.py new file mode 100644 index 0000000000..1ed8b8b9d1 --- /dev/null +++ b/src/openai/types/conversations/input_file_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_input_file_param import ResponseInputFileParam + +InputFileContentParam = ResponseInputFileParam diff --git a/src/openai/types/conversations/input_image_content.py b/src/openai/types/conversations/input_image_content.py index f2587e0adc..4304323c3a 100644 --- a/src/openai/types/conversations/input_image_content.py +++ b/src/openai/types/conversations/input_image_content.py @@ -1,28 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel +from ..responses.response_input_image import ResponseInputImage __all__ = ["InputImageContent"] - -class InputImageContent(BaseModel): - detail: Literal["low", "high", "auto"] - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - file_id: Optional[str] = None - """The ID of the file to be sent to the model.""" - - image_url: Optional[str] = None - """The URL of the image to be sent to the model. - - A fully qualified URL or base64 encoded image in a data URL. - """ - - type: Literal["input_image"] - """The type of the input item. Always `input_image`.""" +InputImageContent = ResponseInputImage diff --git a/src/openai/types/conversations/input_image_content_param.py b/src/openai/types/conversations/input_image_content_param.py new file mode 100644 index 0000000000..a0ef9f545c --- /dev/null +++ b/src/openai/types/conversations/input_image_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_input_image_param import ResponseInputImageParam + +InputImageContentParam = ResponseInputImageParam diff --git a/src/openai/types/conversations/input_text_content.py b/src/openai/types/conversations/input_text_content.py index 5e2daebdc5..cab8b26cb1 100644 --- a/src/openai/types/conversations/input_text_content.py +++ b/src/openai/types/conversations/input_text_content.py @@ -1,15 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal - -from ..._models import BaseModel +from ..responses.response_input_text import ResponseInputText __all__ = ["InputTextContent"] - -class InputTextContent(BaseModel): - text: str - """The text input to the model.""" - - type: Literal["input_text"] - """The type of the input item. Always `input_text`.""" +InputTextContent = ResponseInputText diff --git a/src/openai/types/conversations/input_text_content_param.py b/src/openai/types/conversations/input_text_content_param.py new file mode 100644 index 0000000000..b1fd9a5f1c --- /dev/null +++ b/src/openai/types/conversations/input_text_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_input_text_param import ResponseInputTextParam + +InputTextContentParam = ResponseInputTextParam diff --git a/src/openai/types/conversations/lob_prob.py b/src/openai/types/conversations/lob_prob.py deleted file mode 100644 index f7dcd62a5e..0000000000 --- a/src/openai/types/conversations/lob_prob.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from ..._models import BaseModel -from .top_log_prob import TopLogProb - -__all__ = ["LobProb"] - - -class LobProb(BaseModel): - token: str - - bytes: List[int] - - logprob: float - - top_logprobs: List[TopLogProb] diff --git a/src/openai/types/conversations/message.py b/src/openai/types/conversations/message.py index a070cf2869..95e03c5c00 100644 --- a/src/openai/types/conversations/message.py +++ b/src/openai/types/conversations/message.py @@ -6,26 +6,26 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from .text_content import TextContent -from .refusal_content import RefusalContent -from .input_file_content import InputFileContent -from .input_text_content import InputTextContent -from .input_image_content import InputImageContent -from .output_text_content import OutputTextContent from .summary_text_content import SummaryTextContent from .computer_screenshot_content import ComputerScreenshotContent +from ..responses.response_input_file import ResponseInputFile +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_image import ResponseInputImage +from ..responses.response_output_text import ResponseOutputText +from ..responses.response_output_refusal import ResponseOutputRefusal __all__ = ["Message", "Content"] Content: TypeAlias = Annotated[ Union[ - InputTextContent, - OutputTextContent, + ResponseInputText, + ResponseOutputText, TextContent, SummaryTextContent, - RefusalContent, - InputImageContent, + ResponseOutputRefusal, + ResponseInputImage, ComputerScreenshotContent, - InputFileContent, + ResponseInputFile, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/conversations/output_text_content.py b/src/openai/types/conversations/output_text_content.py index 2ffee76526..cfe9307d74 100644 --- a/src/openai/types/conversations/output_text_content.py +++ b/src/openai/types/conversations/output_text_content.py @@ -1,30 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias +from ..responses.response_output_text import ResponseOutputText -from ..._utils import PropertyInfo -from .lob_prob import LobProb -from ..._models import BaseModel -from .url_citation_body import URLCitationBody -from .file_citation_body import FileCitationBody -from .container_file_citation_body import ContainerFileCitationBody +__all__ = ["OutputTextContent"] -__all__ = ["OutputTextContent", "Annotation"] - -Annotation: TypeAlias = Annotated[ - Union[FileCitationBody, URLCitationBody, ContainerFileCitationBody], PropertyInfo(discriminator="type") -] - - -class OutputTextContent(BaseModel): - annotations: List[Annotation] - """The annotations of the text output.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - logprobs: Optional[List[LobProb]] = None +OutputTextContent = ResponseOutputText diff --git a/src/openai/types/conversations/output_text_content_param.py b/src/openai/types/conversations/output_text_content_param.py new file mode 100644 index 0000000000..dc3e2026f6 --- /dev/null +++ b/src/openai/types/conversations/output_text_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_output_text_param import ResponseOutputTextParam + +OutputTextContentParam = ResponseOutputTextParam diff --git a/src/openai/types/conversations/refusal_content.py b/src/openai/types/conversations/refusal_content.py index 3c8bd5e35f..6206c267dc 100644 --- a/src/openai/types/conversations/refusal_content.py +++ b/src/openai/types/conversations/refusal_content.py @@ -1,15 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal - -from ..._models import BaseModel +from ..responses.response_output_refusal import ResponseOutputRefusal __all__ = ["RefusalContent"] - -class RefusalContent(BaseModel): - refusal: str - """The refusal explanation from the model.""" - - type: Literal["refusal"] - """The type of the refusal. Always `refusal`.""" +RefusalContent = ResponseOutputRefusal diff --git a/src/openai/types/conversations/refusal_content_param.py b/src/openai/types/conversations/refusal_content_param.py new file mode 100644 index 0000000000..9b83da5f2d --- /dev/null +++ b/src/openai/types/conversations/refusal_content_param.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..responses.response_output_refusal_param import ResponseOutputRefusalParam + +RefusalContentParam = ResponseOutputRefusalParam diff --git a/src/openai/types/conversations/top_log_prob.py b/src/openai/types/conversations/top_log_prob.py deleted file mode 100644 index fafca756ae..0000000000 --- a/src/openai/types/conversations/top_log_prob.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List - -from ..._models import BaseModel - -__all__ = ["TopLogProb"] - - -class TopLogProb(BaseModel): - token: str - - bytes: List[int] - - logprob: float diff --git a/src/openai/types/conversations/url_citation_body.py b/src/openai/types/conversations/url_citation_body.py deleted file mode 100644 index 1becb44bc0..0000000000 --- a/src/openai/types/conversations/url_citation_body.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["URLCitationBody"] - - -class URLCitationBody(BaseModel): - end_index: int - """The index of the last character of the URL citation in the message.""" - - start_index: int - """The index of the first character of the URL citation in the message.""" - - title: str - """The title of the web resource.""" - - type: Literal["url_citation"] - """The type of the URL citation. Always `url_citation`.""" - - url: str - """The URL of the web resource.""" diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index edf70c8ad4..74323a735e 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -6,6 +6,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from ..shared.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort from ..shared.response_format_text import ResponseFormatText from ..responses.easy_input_message import EasyInputMessage from ..responses.response_input_text import ResponseInputText @@ -167,6 +168,15 @@ class SamplingParams(BaseModel): max_completion_tokens: Optional[int] = None """The maximum number of tokens in the generated output.""" + reasoning_effort: Optional[ReasoningEffort] = None + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + response_format: Optional[SamplingParamsResponseFormat] = None """An object specifying the format that the model must output. diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index c14360ac80..4e9c1fdeb8 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..shared_params.metadata import Metadata +from ..shared.reasoning_effort import ReasoningEffort from ..responses.easy_input_message_param import EasyInputMessageParam from ..shared_params.response_format_text import ResponseFormatText from ..responses.response_input_text_param import ResponseInputTextParam @@ -163,6 +164,15 @@ class SamplingParams(TypedDict, total=False): max_completion_tokens: int """The maximum number of tokens in the generated output.""" + reasoning_effort: Optional[ReasoningEffort] + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + response_format: SamplingParamsResponseFormat """An object specifying the format that the model must output. diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 8f43494e68..d04d4ff657 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -234,6 +234,15 @@ class DataSourceResponsesSamplingParams(BaseModel): max_completion_tokens: Optional[int] = None """The maximum number of tokens in the generated output.""" + reasoning_effort: Optional[ReasoningEffort] = None + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + seed: Optional[int] = None """A seed value to initialize the randomness, during sampling.""" diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index 35813c8901..6ff897b5de 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -252,6 +252,15 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= max_completion_tokens: int """The maximum number of tokens in the generated output.""" + reasoning_effort: Optional[ReasoningEffort] + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + seed: int """A seed value to initialize the randomness, during sampling.""" diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index c842a5ad2f..defa275c8c 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -234,6 +234,15 @@ class DataSourceResponsesSamplingParams(BaseModel): max_completion_tokens: Optional[int] = None """The maximum number of tokens in the generated output.""" + reasoning_effort: Optional[ReasoningEffort] = None + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + seed: Optional[int] = None """A seed value to initialize the randomness, during sampling.""" diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index 5a5c2efbb3..7fe0e55ace 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -234,6 +234,15 @@ class DataSourceResponsesSamplingParams(BaseModel): max_completion_tokens: Optional[int] = None """The maximum number of tokens in the generated output.""" + reasoning_effort: Optional[ReasoningEffort] = None + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + seed: Optional[int] = None """A seed value to initialize the randomness, during sampling.""" diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index f341296875..a50520f17d 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -234,6 +234,15 @@ class DataSourceResponsesSamplingParams(BaseModel): max_completion_tokens: Optional[int] = None """The maximum number of tokens in the generated output.""" + reasoning_effort: Optional[ReasoningEffort] = None + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + seed: Optional[int] = None """A seed value to initialize the randomness, during sampling.""" diff --git a/src/openai/types/evals/runs/output_item_list_response.py b/src/openai/types/evals/runs/output_item_list_response.py index 72b1049f7b..f774518f3c 100644 --- a/src/openai/types/evals/runs/output_item_list_response.py +++ b/src/openai/types/evals/runs/output_item_list_response.py @@ -1,13 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import builtins -from typing import Dict, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from typing_extensions import Literal +from pydantic import Field as FieldInfo + from ...._models import BaseModel from ..eval_api_error import EvalAPIError -__all__ = ["OutputItemListResponse", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] +__all__ = ["OutputItemListResponse", "Result", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] + + +class Result(BaseModel): + name: str + """The name of the grader.""" + + passed: bool + """Whether the grader considered the output a pass.""" + + score: float + """The numeric score produced by the grader.""" + + sample: Optional[Dict[str, object]] = None + """Optional sample or intermediate data produced by the grader.""" + + type: Optional[str] = None + """The grader type (for example, "string-check-grader").""" + + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + if TYPE_CHECKING: + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... class SampleInput(BaseModel): @@ -91,8 +116,8 @@ class OutputItemListResponse(BaseModel): object: Literal["eval.run.output_item"] """The type of the object. Always "eval.run.output_item".""" - results: List[Dict[str, builtins.object]] - """A list of results from the evaluation run.""" + results: List[Result] + """A list of grader results for this output item.""" run_id: str """The identifier of the evaluation run associated with this output item.""" diff --git a/src/openai/types/evals/runs/output_item_retrieve_response.py b/src/openai/types/evals/runs/output_item_retrieve_response.py index 63aab5565f..d66435bd4f 100644 --- a/src/openai/types/evals/runs/output_item_retrieve_response.py +++ b/src/openai/types/evals/runs/output_item_retrieve_response.py @@ -1,13 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -import builtins -from typing import Dict, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from typing_extensions import Literal +from pydantic import Field as FieldInfo + from ...._models import BaseModel from ..eval_api_error import EvalAPIError -__all__ = ["OutputItemRetrieveResponse", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] +__all__ = ["OutputItemRetrieveResponse", "Result", "Sample", "SampleInput", "SampleOutput", "SampleUsage"] + + +class Result(BaseModel): + name: str + """The name of the grader.""" + + passed: bool + """Whether the grader considered the output a pass.""" + + score: float + """The numeric score produced by the grader.""" + + sample: Optional[Dict[str, object]] = None + """Optional sample or intermediate data produced by the grader.""" + + type: Optional[str] = None + """The grader type (for example, "string-check-grader").""" + + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + if TYPE_CHECKING: + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... class SampleInput(BaseModel): @@ -91,8 +116,8 @@ class OutputItemRetrieveResponse(BaseModel): object: Literal["eval.run.output_item"] """The type of the object. Always "eval.run.output_item".""" - results: List[Dict[str, builtins.object]] - """A list of results from the evaluation run.""" + results: List[Result] + """A list of grader results for this output item.""" run_id: str """The identifier of the evaluation run associated with this output item.""" diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index fc221b8e41..908c6f91d3 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -4,10 +4,18 @@ from typing_extensions import Literal, TypeAlias from ..._models import BaseModel +from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio -__all__ = ["ScoreModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] +__all__ = [ + "ScoreModelGrader", + "Input", + "InputContent", + "InputContentOutputText", + "InputContentInputImage", + "SamplingParams", +] class InputContentOutputText(BaseModel): @@ -51,6 +59,29 @@ class Input(BaseModel): """The type of the message input. Always `message`.""" +class SamplingParams(BaseModel): + max_completions_tokens: Optional[int] = None + """The maximum number of tokens the grader model may generate in its response.""" + + reasoning_effort: Optional[ReasoningEffort] = None + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + + seed: Optional[int] = None + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] = None + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] = None + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + class ScoreModelGrader(BaseModel): input: List[Input] """The input text. This may include template strings.""" @@ -67,5 +98,5 @@ class ScoreModelGrader(BaseModel): range: Optional[List[float]] = None """The range of the score. Defaults to `[0, 1]`.""" - sampling_params: Optional[object] = None + sampling_params: Optional[SamplingParams] = None """The sampling parameters for the model.""" diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 15100bb74b..743944e099 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -2,13 +2,21 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text_param import ResponseInputTextParam from ..responses.response_input_audio_param import ResponseInputAudioParam -__all__ = ["ScoreModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] +__all__ = [ + "ScoreModelGraderParam", + "Input", + "InputContent", + "InputContentOutputText", + "InputContentInputImage", + "SamplingParams", +] class InputContentOutputText(TypedDict, total=False): @@ -57,6 +65,29 @@ class Input(TypedDict, total=False): """The type of the message input. Always `message`.""" +class SamplingParams(TypedDict, total=False): + max_completions_tokens: Optional[int] + """The maximum number of tokens the grader model may generate in its response.""" + + reasoning_effort: Optional[ReasoningEffort] + """ + Constrains effort on reasoning for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently + supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning + effort can result in faster responses and fewer tokens used on reasoning in a + response. + """ + + seed: Optional[int] + """A seed value to initialize the randomness, during sampling.""" + + temperature: Optional[float] + """A higher temperature increases randomness in the outputs.""" + + top_p: Optional[float] + """An alternative to temperature for nucleus sampling; 1.0 includes all tokens.""" + + class ScoreModelGraderParam(TypedDict, total=False): input: Required[Iterable[Input]] """The input text. This may include template strings.""" @@ -73,5 +104,5 @@ class ScoreModelGraderParam(TypedDict, total=False): range: Iterable[float] """The range of the score. Defaults to `[0, 1]`.""" - sampling_params: object + sampling_params: SamplingParams """The sampling parameters for the model.""" From 82602884b61ef2f407f4c5f4fcae7d07243897be Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 14:56:20 +0000 Subject: [PATCH 109/408] chore(types): change optional parameter type from NotGiven to Omit --- src/openai/__init__.py | 4 +- src/openai/_base_client.py | 18 +- src/openai/_client.py | 16 +- src/openai/_qs.py | 14 +- src/openai/_types.py | 31 +- src/openai/_utils/_transform.py | 4 +- src/openai/_utils/_utils.py | 8 +- src/openai/cli/_api/audio.py | 12 +- src/openai/cli/_api/completions.py | 26 +- src/openai/cli/_api/fine_tuning/jobs.py | 25 +- src/openai/cli/_api/image.py | 16 +- src/openai/lib/_parsing/_completions.py | 34 +- src/openai/lib/_parsing/_responses.py | 10 +- src/openai/lib/streaming/chat/_completions.py | 28 +- .../lib/streaming/responses/_responses.py | 24 +- src/openai/resources/audio/speech.py | 22 +- src/openai/resources/audio/transcriptions.py | 196 ++-- src/openai/resources/audio/translations.py | 62 +- src/openai/resources/batches.py | 34 +- src/openai/resources/beta/assistants.py | 122 +-- src/openai/resources/beta/threads/messages.py | 54 +- .../resources/beta/threads/runs/runs.py | 786 +++++++-------- .../resources/beta/threads/runs/steps.py | 34 +- src/openai/resources/beta/threads/threads.py | 514 +++++----- .../resources/chat/completions/completions.py | 814 ++++++++-------- .../resources/chat/completions/messages.py | 18 +- src/openai/resources/completions.py | 266 +++--- src/openai/resources/containers/containers.py | 38 +- .../resources/containers/files/content.py | 6 +- .../resources/containers/files/files.py | 38 +- .../resources/conversations/conversations.py | 26 +- src/openai/resources/conversations/items.py | 42 +- src/openai/resources/embeddings.py | 18 +- src/openai/resources/evals/evals.py | 54 +- .../resources/evals/runs/output_items.py | 26 +- src/openai/resources/evals/runs/runs.py | 46 +- src/openai/resources/files.py | 46 +- .../resources/fine_tuning/alpha/graders.py | 14 +- .../fine_tuning/checkpoints/permissions.py | 30 +- .../resources/fine_tuning/jobs/checkpoints.py | 14 +- src/openai/resources/fine_tuning/jobs/jobs.py | 78 +- src/openai/resources/images.py | 466 +++++---- src/openai/resources/models.py | 14 +- src/openai/resources/moderations.py | 10 +- .../resources/realtime/client_secrets.py | 14 +- src/openai/resources/realtime/realtime.py | 66 +- src/openai/resources/responses/input_items.py | 22 +- src/openai/resources/responses/responses.py | 898 +++++++++--------- src/openai/resources/uploads/parts.py | 6 +- src/openai/resources/uploads/uploads.py | 34 +- .../resources/vector_stores/file_batches.py | 66 +- src/openai/resources/vector_stores/files.py | 86 +- .../resources/vector_stores/vector_stores.py | 90 +- src/openai/types/responses/tool.py | 1 + src/openai/types/responses/tool_param.py | 1 + tests/test_transform.py | 11 +- 56 files changed, 2723 insertions(+), 2730 deletions(-) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index a03b49e0c4..bd01da628d 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -7,7 +7,7 @@ from typing_extensions import override from . import types -from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions from ._models import BaseModel @@ -46,7 +46,9 @@ "ProxiesTypes", "NotGiven", "NOT_GIVEN", + "not_given", "Omit", + "omit", "OpenAIError", "APIError", "APIStatusError", diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index d5f1ab0903..58490e4430 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -42,7 +42,6 @@ from ._qs import Querystring from ._files import to_httpx_files, async_to_httpx_files from ._types import ( - NOT_GIVEN, Body, Omit, Query, @@ -57,6 +56,7 @@ RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, + not_given, ) from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import PYDANTIC_V1, model_copy, model_dump @@ -147,9 +147,9 @@ def __init__( def __init__( self, *, - url: URL | NotGiven = NOT_GIVEN, - json: Body | NotGiven = NOT_GIVEN, - params: Query | NotGiven = NOT_GIVEN, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, ) -> None: self.url = url self.json = json @@ -597,7 +597,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques # we internally support defining a temporary header to override the # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` # see _response.py for implementation details - override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) if is_given(override_cast_to): options.headers = headers return cast(Type[ResponseT], override_cast_to) @@ -827,7 +827,7 @@ def __init__( version: str, base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1373,7 +1373,7 @@ def __init__( base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1850,8 +1850,8 @@ def make_request_options( extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - post_parser: PostParser | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} diff --git a/src/openai/_client.py b/src/openai/_client.py index 2be32fe13f..1485029ddd 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, Any, Union, Mapping, Callable, Awaitable +from typing import TYPE_CHECKING, Any, Mapping, Callable, Awaitable from typing_extensions import Self, override import httpx @@ -11,13 +11,13 @@ from . import _exceptions from ._qs import Querystring from ._types import ( - NOT_GIVEN, Omit, Timeout, NotGiven, Transport, ProxiesTypes, RequestOptions, + not_given, ) from ._utils import ( is_given, @@ -103,7 +103,7 @@ def __init__( webhook_secret: str | None = None, base_url: str | httpx.URL | None = None, websocket_base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -339,9 +339,9 @@ def copy( webhook_secret: str | None = None, websocket_base_url: str | httpx.URL | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -448,7 +448,7 @@ def __init__( webhook_secret: str | None = None, base_url: str | httpx.URL | None = None, websocket_base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -684,9 +684,9 @@ def copy( webhook_secret: str | None = None, websocket_base_url: str | httpx.URL | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, diff --git a/src/openai/_qs.py b/src/openai/_qs.py index 274320ca5e..ada6fd3f72 100644 --- a/src/openai/_qs.py +++ b/src/openai/_qs.py @@ -4,7 +4,7 @@ from urllib.parse import parse_qs, urlencode from typing_extensions import Literal, get_args -from ._types import NOT_GIVEN, NotGiven, NotGivenOr +from ._types import NotGiven, not_given from ._utils import flatten _T = TypeVar("_T") @@ -41,8 +41,8 @@ def stringify( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> str: return urlencode( self.stringify_items( @@ -56,8 +56,8 @@ def stringify_items( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> list[tuple[str, str]]: opts = Options( qs=self, @@ -143,8 +143,8 @@ def __init__( self, qs: Querystring = _qs, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> None: self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/openai/_types.py b/src/openai/_types.py index 0e8ffa12aa..2387d7e01c 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -118,18 +118,21 @@ class RequestOptions(TypedDict, total=False): # Sentinel class used until PEP 0661 is accepted class NotGiven: """ - A sentinel singleton class used to distinguish omitted keyword arguments - from those passed in with the value None (which may have different behavior). + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + def create(timeout: Timeout | None | NotGiven = not_given): ... - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior ``` """ @@ -141,13 +144,14 @@ def __repr__(self) -> str: return "NOT_GIVEN" -NotGivenOr = Union[_T, NotGiven] +not_given = NotGiven() +# for backwards compatibility: NOT_GIVEN = NotGiven() class Omit: - """In certain situations you need to be able to represent a case where a default value has - to be explicitly removed and `None` is not an appropriate substitute, for example: + """ + To explicitly omit something from being sent in a request, use `omit`. ```py # as the default `Content-Type` header is `application/json` that will be sent @@ -157,8 +161,8 @@ class Omit: # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' client.post(..., headers={"Content-Type": "multipart/form-data"}) - # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={"Content-Type": Omit()}) + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) ``` """ @@ -166,6 +170,11 @@ def __bool__(self) -> Literal[False]: return False +omit = Omit() + +Omittable = Union[_T, Omit] + + @runtime_checkable class ModelBuilderProtocol(Protocol): @classmethod diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index bc262ea339..414f38c340 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -268,7 +268,7 @@ def _transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue @@ -434,7 +434,7 @@ async def _async_transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 4a23c96c0a..cddf2c8da4 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -22,7 +22,7 @@ import sniffio -from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._types import Omit, NotGiven, FileTypes, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -67,7 +67,7 @@ def _extract_items( try: key = path[index] except IndexError: - if isinstance(obj, NotGiven): + if not is_given(obj): # no value was provided - we can safely ignore return [] @@ -130,8 +130,8 @@ def _extract_items( return [] -def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) # Type safe methods for narrowing types with TypeVars. diff --git a/src/openai/cli/_api/audio.py b/src/openai/cli/_api/audio.py index 269c67df28..e7c3734e75 100644 --- a/src/openai/cli/_api/audio.py +++ b/src/openai/cli/_api/audio.py @@ -5,7 +5,7 @@ from argparse import ArgumentParser from .._utils import get_client, print_model -from ..._types import NOT_GIVEN +from ..._types import omit from .._models import BaseModel from .._progress import BufferReader from ...types.audio import Transcription @@ -72,9 +72,9 @@ def transcribe(args: CLITranscribeArgs) -> None: get_client().audio.transcriptions.create( file=(args.file, buffer_reader), model=args.model, - language=args.language or NOT_GIVEN, - temperature=args.temperature or NOT_GIVEN, - prompt=args.prompt or NOT_GIVEN, + language=args.language or omit, + temperature=args.temperature or omit, + prompt=args.prompt or omit, # casts required because the API is typed for enums # but we don't want to validate that here for forwards-compat response_format=cast(Any, args.response_format), @@ -95,8 +95,8 @@ def translate(args: CLITranslationArgs) -> None: get_client().audio.translations.create( file=(args.file, buffer_reader), model=args.model, - temperature=args.temperature or NOT_GIVEN, - prompt=args.prompt or NOT_GIVEN, + temperature=args.temperature or omit, + prompt=args.prompt or omit, # casts required because the API is typed for enums # but we don't want to validate that here for forwards-compat response_format=cast(Any, args.response_format), diff --git a/src/openai/cli/_api/completions.py b/src/openai/cli/_api/completions.py index cbdb35bf3a..b22ecde9ef 100644 --- a/src/openai/cli/_api/completions.py +++ b/src/openai/cli/_api/completions.py @@ -8,7 +8,7 @@ from openai.types.completion import Completion from .._utils import get_client -from ..._types import NOT_GIVEN, NotGivenOr +from ..._types import Omittable, omit from ..._utils import is_given from .._errors import CLIError from .._models import BaseModel @@ -95,18 +95,18 @@ class CLICompletionCreateArgs(BaseModel): stream: bool = False prompt: Optional[str] = None - n: NotGivenOr[int] = NOT_GIVEN - stop: NotGivenOr[str] = NOT_GIVEN - user: NotGivenOr[str] = NOT_GIVEN - echo: NotGivenOr[bool] = NOT_GIVEN - suffix: NotGivenOr[str] = NOT_GIVEN - best_of: NotGivenOr[int] = NOT_GIVEN - top_p: NotGivenOr[float] = NOT_GIVEN - logprobs: NotGivenOr[int] = NOT_GIVEN - max_tokens: NotGivenOr[int] = NOT_GIVEN - temperature: NotGivenOr[float] = NOT_GIVEN - presence_penalty: NotGivenOr[float] = NOT_GIVEN - frequency_penalty: NotGivenOr[float] = NOT_GIVEN + n: Omittable[int] = omit + stop: Omittable[str] = omit + user: Omittable[str] = omit + echo: Omittable[bool] = omit + suffix: Omittable[str] = omit + best_of: Omittable[int] = omit + top_p: Omittable[float] = omit + logprobs: Omittable[int] = omit + max_tokens: Omittable[int] = omit + temperature: Omittable[float] = omit + presence_penalty: Omittable[float] = omit + frequency_penalty: Omittable[float] = omit class CLICompletions: diff --git a/src/openai/cli/_api/fine_tuning/jobs.py b/src/openai/cli/_api/fine_tuning/jobs.py index 806fa0f788..a4e429108a 100644 --- a/src/openai/cli/_api/fine_tuning/jobs.py +++ b/src/openai/cli/_api/fine_tuning/jobs.py @@ -5,7 +5,8 @@ from argparse import ArgumentParser from ..._utils import get_client, print_model -from ...._types import NOT_GIVEN, NotGivenOr +from ...._types import Omittable, omit +from ...._utils import is_given from ..._models import BaseModel from ....pagination import SyncCursorPage from ....types.fine_tuning import ( @@ -105,9 +106,9 @@ def register(subparser: _SubParsersAction[ArgumentParser]) -> None: class CLIFineTuningJobsCreateArgs(BaseModel): model: str training_file: str - hyperparameters: NotGivenOr[str] = NOT_GIVEN - suffix: NotGivenOr[str] = NOT_GIVEN - validation_file: NotGivenOr[str] = NOT_GIVEN + hyperparameters: Omittable[str] = omit + suffix: Omittable[str] = omit + validation_file: Omittable[str] = omit class CLIFineTuningJobsRetrieveArgs(BaseModel): @@ -115,8 +116,8 @@ class CLIFineTuningJobsRetrieveArgs(BaseModel): class CLIFineTuningJobsListArgs(BaseModel): - after: NotGivenOr[str] = NOT_GIVEN - limit: NotGivenOr[int] = NOT_GIVEN + after: Omittable[str] = omit + limit: Omittable[int] = omit class CLIFineTuningJobsCancelArgs(BaseModel): @@ -125,14 +126,14 @@ class CLIFineTuningJobsCancelArgs(BaseModel): class CLIFineTuningJobsListEventsArgs(BaseModel): id: str - after: NotGivenOr[str] = NOT_GIVEN - limit: NotGivenOr[int] = NOT_GIVEN + after: Omittable[str] = omit + limit: Omittable[int] = omit class CLIFineTuningJobs: @staticmethod def create(args: CLIFineTuningJobsCreateArgs) -> None: - hyperparameters = json.loads(str(args.hyperparameters)) if args.hyperparameters is not NOT_GIVEN else NOT_GIVEN + hyperparameters = json.loads(str(args.hyperparameters)) if is_given(args.hyperparameters) else omit fine_tuning_job: FineTuningJob = get_client().fine_tuning.jobs.create( model=args.model, training_file=args.training_file, @@ -150,7 +151,7 @@ def retrieve(args: CLIFineTuningJobsRetrieveArgs) -> None: @staticmethod def list(args: CLIFineTuningJobsListArgs) -> None: fine_tuning_jobs: SyncCursorPage[FineTuningJob] = get_client().fine_tuning.jobs.list( - after=args.after or NOT_GIVEN, limit=args.limit or NOT_GIVEN + after=args.after or omit, limit=args.limit or omit ) print_model(fine_tuning_jobs) @@ -163,7 +164,7 @@ def cancel(args: CLIFineTuningJobsCancelArgs) -> None: def list_events(args: CLIFineTuningJobsListEventsArgs) -> None: fine_tuning_job_events: SyncCursorPage[FineTuningJobEvent] = get_client().fine_tuning.jobs.list_events( fine_tuning_job_id=args.id, - after=args.after or NOT_GIVEN, - limit=args.limit or NOT_GIVEN, + after=args.after or omit, + limit=args.limit or omit, ) print_model(fine_tuning_job_events) diff --git a/src/openai/cli/_api/image.py b/src/openai/cli/_api/image.py index 3e2a0a90f1..1d0cf810c1 100644 --- a/src/openai/cli/_api/image.py +++ b/src/openai/cli/_api/image.py @@ -4,7 +4,7 @@ from argparse import ArgumentParser from .._utils import get_client, print_model -from ..._types import NOT_GIVEN, NotGiven, NotGivenOr +from ..._types import Omit, Omittable, omit from .._models import BaseModel from .._progress import BufferReader @@ -63,7 +63,7 @@ class CLIImageCreateArgs(BaseModel): num_images: int size: str response_format: str - model: NotGivenOr[str] = NOT_GIVEN + model: Omittable[str] = omit class CLIImageCreateVariationArgs(BaseModel): @@ -71,7 +71,7 @@ class CLIImageCreateVariationArgs(BaseModel): num_images: int size: str response_format: str - model: NotGivenOr[str] = NOT_GIVEN + model: Omittable[str] = omit class CLIImageEditArgs(BaseModel): @@ -80,8 +80,8 @@ class CLIImageEditArgs(BaseModel): size: str response_format: str prompt: str - mask: NotGivenOr[str] = NOT_GIVEN - model: NotGivenOr[str] = NOT_GIVEN + mask: Omittable[str] = omit + model: Omittable[str] = omit class CLIImage: @@ -119,8 +119,8 @@ def edit(args: CLIImageEditArgs) -> None: with open(args.image, "rb") as file_reader: buffer_reader = BufferReader(file_reader.read(), desc="Image upload progress") - if isinstance(args.mask, NotGiven): - mask: NotGivenOr[BufferReader] = NOT_GIVEN + if isinstance(args.mask, Omit): + mask: Omittable[BufferReader] = omit else: with open(args.mask, "rb") as file_reader: mask = BufferReader(file_reader.read(), desc="Mask progress") @@ -130,7 +130,7 @@ def edit(args: CLIImageEditArgs) -> None: prompt=args.prompt, image=("image", buffer_reader), n=args.num_images, - mask=("mask", mask) if not isinstance(mask, NotGiven) else mask, + mask=("mask", mask) if not isinstance(mask, Omit) else mask, # casts required because the API is typed for enums # but we don't want to validate that here for forwards-compat size=cast(Any, args.size), diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index 4b8b78b70a..7903732a4a 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -8,7 +8,7 @@ import pydantic from .._tools import PydanticFunctionTool -from ..._types import NOT_GIVEN, NotGiven +from ..._types import Omit, omit from ..._utils import is_dict, is_given from ..._compat import PYDANTIC_V1, model_parse_json from ..._models import construct_type_unchecked @@ -53,20 +53,20 @@ def is_strict_chat_completion_tool_param( def select_strict_chat_completion_tools( - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, -) -> Iterable[ChatCompletionFunctionToolParam] | NotGiven: + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, +) -> Iterable[ChatCompletionFunctionToolParam] | Omit: """Select only the strict ChatCompletionFunctionToolParams from the given tools.""" if not is_given(tools): - return NOT_GIVEN + return omit return [t for t in tools if is_strict_chat_completion_tool_param(t)] def validate_input_tools( - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, -) -> Iterable[ChatCompletionFunctionToolParam] | NotGiven: + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, +) -> Iterable[ChatCompletionFunctionToolParam] | Omit: if not is_given(tools): - return NOT_GIVEN + return omit for tool in tools: if tool["type"] != "function": @@ -85,8 +85,8 @@ def validate_input_tools( def parse_chat_completion( *, - response_format: type[ResponseFormatT] | completion_create_params.ResponseFormat | NotGiven, - input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, + response_format: type[ResponseFormatT] | completion_create_params.ResponseFormat | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, chat_completion: ChatCompletion | ParsedChatCompletion[object], ) -> ParsedChatCompletion[ResponseFormatT]: if is_given(input_tools): @@ -192,7 +192,7 @@ def parse_function_tool_arguments( def maybe_parse_content( *, - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, message: ChatCompletionMessage | ParsedChatCompletionMessage[object], ) -> ResponseFormatT | None: if has_rich_response_format(response_format) and message.content and not message.refusal: @@ -202,7 +202,7 @@ def maybe_parse_content( def solve_response_format_t( - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, ) -> type[ResponseFormatT]: """Return the runtime type for the given response format. @@ -217,8 +217,8 @@ def solve_response_format_t( def has_parseable_input( *, - response_format: type | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, + response_format: type | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, ) -> bool: if has_rich_response_format(response_format): return True @@ -231,7 +231,7 @@ def has_parseable_input( def has_rich_response_format( - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, ) -> TypeGuard[type[ResponseFormatT]]: if not is_given(response_format): return False @@ -271,10 +271,10 @@ def _parse_content(response_format: type[ResponseFormatT], content: str) -> Resp def type_to_response_format_param( - response_format: type | completion_create_params.ResponseFormat | NotGiven, -) -> ResponseFormatParam | NotGiven: + response_format: type | completion_create_params.ResponseFormat | Omit, +) -> ResponseFormatParam | Omit: if not is_given(response_format): - return NOT_GIVEN + return omit if is_response_format_param(response_format): return response_format diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index b6ebde0e8e..8a1bf3cf2c 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -7,7 +7,7 @@ import pydantic from .._tools import ResponsesPydanticFunctionTool -from ..._types import NotGiven +from ..._types import Omit from ..._utils import is_given from ..._compat import PYDANTIC_V1, model_parse_json from ..._models import construct_type_unchecked @@ -52,8 +52,8 @@ def type_to_text_format_param(type_: type) -> ResponseFormatTextConfigParam: def parse_response( *, - text_format: type[TextFormatT] | NotGiven, - input_tools: Iterable[ToolParam] | NotGiven | None, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit | None, response: Response | ParsedResponse[object], ) -> ParsedResponse[TextFormatT]: solved_t = solve_response_format_t(text_format) @@ -130,7 +130,7 @@ def parse_response( ) -def parse_text(text: str, text_format: type[TextFormatT] | NotGiven) -> TextFormatT | None: +def parse_text(text: str, text_format: type[TextFormatT] | Omit) -> TextFormatT | None: if not is_given(text_format): return None @@ -156,7 +156,7 @@ def get_input_tool_by_name(*, input_tools: Iterable[ToolParam], name: str) -> Fu def parse_function_tool_arguments( *, - input_tools: Iterable[ToolParam] | NotGiven | None, + input_tools: Iterable[ToolParam] | Omit | None, function_call: ParsedResponseFunctionToolCall | ResponseFunctionToolCall, ) -> object: if input_tools is None or not is_given(input_tools): diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index 52a6a550b2..c4610e2120 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -23,7 +23,7 @@ FunctionToolCallArgumentsDeltaEvent, ) from .._deltas import accumulate_delta -from ...._types import NOT_GIVEN, IncEx, NotGiven +from ...._types import Omit, IncEx, omit from ...._utils import is_given, consume_sync_iterator, consume_async_iterator from ...._compat import model_dump from ...._models import build, construct_type @@ -57,8 +57,8 @@ def __init__( self, *, raw_stream: Stream[ChatCompletionChunk], - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, ) -> None: self._raw_stream = raw_stream self._response = raw_stream.response @@ -138,8 +138,8 @@ def __init__( self, api_request: Callable[[], Stream[ChatCompletionChunk]], *, - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, ) -> None: self.__stream: ChatCompletionStream[ResponseFormatT] | None = None self.__api_request = api_request @@ -180,8 +180,8 @@ def __init__( self, *, raw_stream: AsyncStream[ChatCompletionChunk], - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, ) -> None: self._raw_stream = raw_stream self._response = raw_stream.response @@ -261,8 +261,8 @@ def __init__( self, api_request: Awaitable[AsyncStream[ChatCompletionChunk]], *, - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, - input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit, ) -> None: self.__stream: AsyncChatCompletionStream[ResponseFormatT] | None = None self.__api_request = api_request @@ -314,15 +314,15 @@ class ChatCompletionStreamState(Generic[ResponseFormatT]): def __init__( self, *, - input_tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven = NOT_GIVEN, + input_tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit = omit, ) -> None: self.__current_completion_snapshot: ParsedChatCompletionSnapshot | None = None self.__choice_event_states: list[ChoiceEventState] = [] self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else [] self._response_format = response_format - self._rich_response_format: type | NotGiven = response_format if inspect.isclass(response_format) else NOT_GIVEN + self._rich_response_format: type | Omit = response_format if inspect.isclass(response_format) else omit def get_final_completion(self) -> ParsedChatCompletion[ResponseFormatT]: """Parse the final completion object. @@ -599,7 +599,7 @@ def get_done_events( *, choice_chunk: ChoiceChunk, choice_snapshot: ParsedChoiceSnapshot, - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] @@ -639,7 +639,7 @@ def _content_done_events( self, *, choice_snapshot: ParsedChoiceSnapshot, - response_format: type[ResponseFormatT] | ResponseFormatParam | NotGiven, + response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, ) -> list[ChatCompletionStreamEvent[ResponseFormatT]]: events_to_fire: list[ChatCompletionStreamEvent[ResponseFormatT]] = [] diff --git a/src/openai/lib/streaming/responses/_responses.py b/src/openai/lib/streaming/responses/_responses.py index d45664de45..6975a9260d 100644 --- a/src/openai/lib/streaming/responses/_responses.py +++ b/src/openai/lib/streaming/responses/_responses.py @@ -13,7 +13,7 @@ ResponseTextDeltaEvent, ResponseFunctionCallArgumentsDeltaEvent, ) -from ...._types import NOT_GIVEN, NotGiven +from ...._types import Omit, omit from ...._utils import is_given, consume_sync_iterator, consume_async_iterator from ...._models import build, construct_type_unchecked from ...._streaming import Stream, AsyncStream @@ -32,8 +32,8 @@ def __init__( self, *, raw_stream: Stream[RawResponseStreamEvent], - text_format: type[TextFormatT] | NotGiven, - input_tools: Iterable[ToolParam] | NotGiven, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, starting_after: int | None, ) -> None: self._raw_stream = raw_stream @@ -97,8 +97,8 @@ def __init__( self, api_request: Callable[[], Stream[RawResponseStreamEvent]], *, - text_format: type[TextFormatT] | NotGiven, - input_tools: Iterable[ToolParam] | NotGiven, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, starting_after: int | None, ) -> None: self.__stream: ResponseStream[TextFormatT] | None = None @@ -134,8 +134,8 @@ def __init__( self, *, raw_stream: AsyncStream[RawResponseStreamEvent], - text_format: type[TextFormatT] | NotGiven, - input_tools: Iterable[ToolParam] | NotGiven, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, starting_after: int | None, ) -> None: self._raw_stream = raw_stream @@ -199,8 +199,8 @@ def __init__( self, api_request: Awaitable[AsyncStream[RawResponseStreamEvent]], *, - text_format: type[TextFormatT] | NotGiven, - input_tools: Iterable[ToolParam] | NotGiven, + text_format: type[TextFormatT] | Omit, + input_tools: Iterable[ToolParam] | Omit, starting_after: int | None, ) -> None: self.__stream: AsyncResponseStream[TextFormatT] | None = None @@ -235,14 +235,14 @@ class ResponseStreamState(Generic[TextFormatT]): def __init__( self, *, - input_tools: Iterable[ToolParam] | NotGiven, - text_format: type[TextFormatT] | NotGiven, + input_tools: Iterable[ToolParam] | Omit, + text_format: type[TextFormatT] | Omit, ) -> None: self.__current_snapshot: ParsedResponseSnapshot | None = None self._completed_response: ParsedResponse[TextFormatT] | None = None self._input_tools = [tool for tool in input_tools] if is_given(input_tools) else [] self._text_format = text_format - self._rich_text_format: type | NotGiven = text_format if inspect.isclass(text_format) else NOT_GIVEN + self._rich_text_format: type | Omit = text_format if inspect.isclass(text_format) else omit def handle_event(self, event: RawResponseStreamEvent) -> List[ResponseStreamEvent[TextFormatT]]: self.__current_snapshot = snapshot = self.accumulate_event(event) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 64ce5eec49..992fb5971a 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -8,7 +8,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -53,16 +53,16 @@ def create( voice: Union[ str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] ], - instructions: str | NotGiven = NOT_GIVEN, - response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, - speed: float | NotGiven = NOT_GIVEN, - stream_format: Literal["sse", "audio"] | NotGiven = NOT_GIVEN, + instructions: str | Omit = omit, + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, + speed: float | Omit = omit, + stream_format: Literal["sse", "audio"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -149,16 +149,16 @@ async def create( voice: Union[ str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] ], - instructions: str | NotGiven = NOT_GIVEN, - response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | NotGiven = NOT_GIVEN, - speed: float | NotGiven = NOT_GIVEN, - stream_format: Literal["sse", "audio"] | NotGiven = NOT_GIVEN, + instructions: str | Omit = omit, + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, + speed: float | Omit = omit, + stream_format: Literal["sse", "audio"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ Generates audio from the input text. diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 208f6e8b05..1fe8866562 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -10,7 +10,7 @@ from ... import _legacy_response from ...types import AudioResponseFormat -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given from ..._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -57,19 +57,19 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + response_format: Union[Literal["json"], Omit] = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Transcription: ... @overload @@ -78,19 +78,19 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, response_format: Literal["verbose_json"], - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + language: str | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranscriptionVerbose: ... @overload @@ -99,19 +99,19 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, response_format: Literal["text", "srt", "vtt"], - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: ... @overload @@ -121,19 +121,19 @@ def create( file: FileTypes, model: Union[str, AudioModel], stream: Literal[True], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[TranscriptionStreamEvent]: """ Transcribes audio into the input language. @@ -209,19 +209,19 @@ def create( file: FileTypes, model: Union[str, AudioModel], stream: bool, - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranscriptionCreateResponse | Stream[TranscriptionStreamEvent]: """ Transcribes audio into the input language. @@ -296,20 +296,20 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str | Transcription | TranscriptionVerbose | Stream[TranscriptionStreamEvent]: body = deepcopy_minimal( { @@ -374,20 +374,20 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[Literal["json"], Omit] = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranscriptionCreateResponse: """ Transcribes audio into the input language. @@ -457,19 +457,19 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, response_format: Literal["verbose_json"], - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + language: str | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranscriptionVerbose: ... @overload @@ -478,19 +478,19 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, response_format: Literal["text", "srt", "vtt"], - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + language: str | Omit = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: ... @overload @@ -500,19 +500,19 @@ async def create( file: FileTypes, model: Union[str, AudioModel], stream: Literal[True], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[TranscriptionStreamEvent]: """ Transcribes audio into the input language. @@ -588,19 +588,19 @@ async def create( file: FileTypes, model: Union[str, AudioModel], stream: bool, - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranscriptionCreateResponse | AsyncStream[TranscriptionStreamEvent]: """ Transcribes audio into the input language. @@ -675,20 +675,20 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], - chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | NotGiven = NOT_GIVEN, - include: List[TranscriptionInclude] | NotGiven = NOT_GIVEN, - language: str | NotGiven = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, - timestamp_granularities: List[Literal["word", "segment"]] | NotGiven = NOT_GIVEN, + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + include: List[TranscriptionInclude] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Transcription | TranscriptionVerbose | str | AsyncStream[TranscriptionStreamEvent]: body = deepcopy_minimal( { @@ -764,9 +764,9 @@ def __init__(self, transcriptions: AsyncTranscriptions) -> None: def _get_response_format_type( - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven, + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | Omit, ) -> type[Transcription | TranscriptionVerbose | str]: - if isinstance(response_format, NotGiven) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] + if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] return Transcription if response_format == "json": diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 28b577ce2e..a4f844db13 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -9,7 +9,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -52,15 +52,15 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], - response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + response_format: Union[Literal["json"], Omit] = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Translation: ... @overload @@ -70,14 +70,14 @@ def create( file: FileTypes, model: Union[str, AudioModel], response_format: Literal["verbose_json"], - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + prompt: str | Omit = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranslationVerbose: ... @overload @@ -87,14 +87,14 @@ def create( file: FileTypes, model: Union[str, AudioModel], response_format: Literal["text", "srt", "vtt"], - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + prompt: str | Omit = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: ... def create( @@ -102,15 +102,15 @@ def create( *, file: FileTypes, model: Union[str, AudioModel], - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[Literal["json", "text", "srt", "verbose_json", "vtt"], NotGiven] = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + prompt: str | Omit = omit, + response_format: Union[Literal["json", "text", "srt", "verbose_json", "vtt"], Omit] = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Translation | TranslationVerbose | str: """ Translates audio into English. @@ -195,15 +195,15 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], - response_format: Union[Literal["json"], NotGiven] = NOT_GIVEN, - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + response_format: Union[Literal["json"], Omit] = omit, + prompt: str | Omit = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Translation: ... @overload @@ -213,14 +213,14 @@ async def create( file: FileTypes, model: Union[str, AudioModel], response_format: Literal["verbose_json"], - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + prompt: str | Omit = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranslationVerbose: ... @overload @@ -230,14 +230,14 @@ async def create( file: FileTypes, model: Union[str, AudioModel], response_format: Literal["text", "srt", "vtt"], - prompt: str | NotGiven = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + prompt: str | Omit = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: ... async def create( @@ -245,15 +245,15 @@ async def create( *, file: FileTypes, model: Union[str, AudioModel], - prompt: str | NotGiven = NOT_GIVEN, - response_format: Union[AudioResponseFormat, NotGiven] = NOT_GIVEN, - temperature: float | NotGiven = NOT_GIVEN, + prompt: str | Omit = omit, + response_format: Union[AudioResponseFormat, Omit] = omit, + temperature: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Translation | TranslationVerbose | str: """ Translates audio into English. @@ -349,9 +349,9 @@ def __init__(self, translations: AsyncTranslations) -> None: def _get_response_format_type( - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | NotGiven, + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | Omit, ) -> type[Translation | TranslationVerbose | str]: - if isinstance(response_format, NotGiven) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] + if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] return Translation if response_format == "json": diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 2340bd2e32..afc7fa6eb9 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -9,7 +9,7 @@ from .. import _legacy_response from ..types import batch_list_params, batch_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -48,14 +48,14 @@ def create( completion_window: Literal["24h"], endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - output_expires_after: batch_create_params.OutputExpiresAfter | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + output_expires_after: batch_create_params.OutputExpiresAfter | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Batch: """ Creates and executes a batch from an uploaded file of requests @@ -124,7 +124,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Batch: """ Retrieves a batch. @@ -151,14 +151,14 @@ def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[Batch]: """List your organization's batches. @@ -209,7 +209,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Batch: """Cancels an in-progress batch. @@ -263,14 +263,14 @@ async def create( completion_window: Literal["24h"], endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], input_file_id: str, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - output_expires_after: batch_create_params.OutputExpiresAfter | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + output_expires_after: batch_create_params.OutputExpiresAfter | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Batch: """ Creates and executes a batch from an uploaded file of requests @@ -339,7 +339,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Batch: """ Retrieves a batch. @@ -366,14 +366,14 @@ async def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Batch, AsyncCursorPage[Batch]]: """List your organization's batches. @@ -424,7 +424,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Batch: """Cancels an in-progress batch. diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index fe0c99c88a..ddac9a79cb 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -8,7 +8,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -55,22 +55,22 @@ def create( self, *, model: Union[str, ChatModel], - description: Optional[str] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_create_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Assistant: """ Create an assistant with a model and instructions. @@ -184,7 +184,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Assistant: """ Retrieves an assistant. @@ -213,9 +213,9 @@ def update( self, assistant_id: str, *, - description: Optional[str] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, model: Union[ str, Literal[ @@ -263,20 +263,20 @@ def update( "gpt-3.5-turbo-16k-0613", ], ] - | NotGiven = NOT_GIVEN, - name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, + | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_update_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Assistant: """Modifies an assistant. @@ -387,16 +387,16 @@ def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[Assistant]: """Returns a list of assistants. @@ -458,7 +458,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AssistantDeleted: """ Delete an assistant. @@ -508,22 +508,22 @@ async def create( self, *, model: Union[str, ChatModel], - description: Optional[str] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_resources: Optional[assistant_create_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_create_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Assistant: """ Create an assistant with a model and instructions. @@ -637,7 +637,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Assistant: """ Retrieves an assistant. @@ -666,9 +666,9 @@ async def update( self, assistant_id: str, *, - description: Optional[str] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + instructions: Optional[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, model: Union[ str, Literal[ @@ -716,20 +716,20 @@ async def update( "gpt-3.5-turbo-16k-0613", ], ] - | NotGiven = NOT_GIVEN, - name: Optional[str] | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_resources: Optional[assistant_update_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Iterable[AssistantToolParam] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, + | Omit = omit, + name: Optional[str] | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_resources: Optional[assistant_update_params.ToolResources] | Omit = omit, + tools: Iterable[AssistantToolParam] | Omit = omit, + top_p: Optional[float] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Assistant: """Modifies an assistant. @@ -840,16 +840,16 @@ async def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Assistant, AsyncCursorPage[Assistant]]: """Returns a list of assistants. @@ -911,7 +911,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AssistantDeleted: """ Delete an assistant. diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index 8903ff0316..d94ecca9a2 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -9,7 +9,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -55,14 +55,14 @@ def create( *, content: Union[str, Iterable[MessageContentPartParam]], role: Literal["user", "assistant"], - attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + attachments: Optional[Iterable[message_create_params.Attachment]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Create a message. @@ -126,7 +126,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Retrieve a message. @@ -159,13 +159,13 @@ def update( message_id: str, *, thread_id: str, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Modifies a message. @@ -205,17 +205,17 @@ def list( self, thread_id: str, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - run_id: str | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + run_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[Message]: """ Returns a list of messages for a given thread. @@ -283,7 +283,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageDeleted: """ Deletes a message. @@ -338,14 +338,14 @@ async def create( *, content: Union[str, Iterable[MessageContentPartParam]], role: Literal["user", "assistant"], - attachments: Optional[Iterable[message_create_params.Attachment]] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + attachments: Optional[Iterable[message_create_params.Attachment]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Create a message. @@ -409,7 +409,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Retrieve a message. @@ -442,13 +442,13 @@ async def update( message_id: str, *, thread_id: str, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Modifies a message. @@ -488,17 +488,17 @@ def list( self, thread_id: str, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - run_id: str | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + run_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Message, AsyncCursorPage[Message]]: """ Returns a list of messages for a given thread. @@ -566,7 +566,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageDeleted: """ Deletes a message. diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index e97d519a40..ec2dfa84cd 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -18,7 +18,7 @@ StepsWithStreamingResponse, AsyncStepsWithStreamingResponse, ) -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, omit, not_given from ....._utils import ( is_given, required_args, @@ -89,29 +89,29 @@ def create( thread_id: str, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Create a run. @@ -240,28 +240,28 @@ def create( *, assistant_id: str, stream: Literal[True], - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[AssistantStreamEvent]: """ Create a run. @@ -390,28 +390,28 @@ def create( *, assistant_id: str, stream: bool, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | Stream[AssistantStreamEvent]: """ Create a run. @@ -539,29 +539,29 @@ def create( thread_id: str, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | Stream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") @@ -613,7 +613,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Retrieves a run. @@ -646,13 +646,13 @@ def update( run_id: str, *, thread_id: str, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Modifies a run. @@ -692,16 +692,16 @@ def list( self, thread_id: str, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[Run]: """ Returns a list of runs belonging to a thread. @@ -766,7 +766,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Cancels a run that is `in_progress`. @@ -798,23 +798,23 @@ def create_and_poll( self, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -870,21 +870,21 @@ def create_and_stream( self, *, assistant_id: str, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -902,21 +902,21 @@ def create_and_stream( self, *, assistant_id: str, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -934,21 +934,21 @@ def create_and_stream( self, *, assistant_id: str, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1010,8 +1010,8 @@ def poll( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + poll_interval_ms: int | Omit = omit, ) -> Run: """ A helper to poll a run status until it reaches a terminal state. More @@ -1054,22 +1054,22 @@ def stream( self, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1087,22 +1087,22 @@ def stream( self, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1120,22 +1120,22 @@ def stream( self, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1201,13 +1201,13 @@ def submit_tool_outputs( *, thread_id: str, tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ When a run has the `status: "requires_action"` and `required_action.type` is @@ -1246,7 +1246,7 @@ def submit_tool_outputs( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[AssistantStreamEvent]: """ When a run has the `status: "requires_action"` and `required_action.type` is @@ -1285,7 +1285,7 @@ def submit_tool_outputs( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | Stream[AssistantStreamEvent]: """ When a run has the `status: "requires_action"` and `required_action.type` is @@ -1319,13 +1319,13 @@ def submit_tool_outputs( *, thread_id: str, tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | Stream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") @@ -1358,7 +1358,7 @@ def submit_tool_outputs_and_poll( tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], run_id: str, thread_id: str, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1519,29 +1519,29 @@ async def create( thread_id: str, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Create a run. @@ -1670,28 +1670,28 @@ async def create( *, assistant_id: str, stream: Literal[True], - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[AssistantStreamEvent]: """ Create a run. @@ -1820,28 +1820,28 @@ async def create( *, assistant_id: str, stream: bool, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | AsyncStream[AssistantStreamEvent]: """ Create a run. @@ -1970,29 +1970,29 @@ async def create( thread_id: str, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | AsyncStream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") @@ -2044,7 +2044,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Retrieves a run. @@ -2077,13 +2077,13 @@ async def update( run_id: str, *, thread_id: str, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Modifies a run. @@ -2123,16 +2123,16 @@ def list( self, thread_id: str, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Run, AsyncCursorPage[Run]]: """ Returns a list of runs belonging to a thread. @@ -2197,7 +2197,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Cancels a run that is `in_progress`. @@ -2229,23 +2229,23 @@ async def create_and_poll( self, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2301,20 +2301,20 @@ def create_and_stream( self, *, assistant_id: str, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2332,20 +2332,20 @@ def create_and_stream( self, *, assistant_id: str, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AsyncAssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2363,20 +2363,20 @@ def create_and_stream( self, *, assistant_id: str, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AsyncAssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2439,8 +2439,8 @@ async def poll( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + poll_interval_ms: int | Omit = omit, ) -> Run: """ A helper to poll a run status until it reaches a terminal state. More @@ -2483,21 +2483,21 @@ def stream( self, *, assistant_id: str, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -2515,22 +2515,22 @@ def stream( self, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AsyncAssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2548,22 +2548,22 @@ def stream( self, *, assistant_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - additional_instructions: Optional[str] | NotGiven = NOT_GIVEN, - additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[run_create_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, + additional_instructions: Optional[str] | Omit = omit, + additional_messages: Optional[Iterable[run_create_params.AdditionalMessage]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[run_create_params.TruncationStrategy] | Omit = omit, thread_id: str, event_handler: AsyncAssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -2631,13 +2631,13 @@ async def submit_tool_outputs( *, thread_id: str, tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ When a run has the `status: "requires_action"` and `required_action.type` is @@ -2676,7 +2676,7 @@ async def submit_tool_outputs( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[AssistantStreamEvent]: """ When a run has the `status: "requires_action"` and `required_action.type` is @@ -2715,7 +2715,7 @@ async def submit_tool_outputs( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | AsyncStream[AssistantStreamEvent]: """ When a run has the `status: "requires_action"` and `required_action.type` is @@ -2749,13 +2749,13 @@ async def submit_tool_outputs( *, thread_id: str, tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | AsyncStream[AssistantStreamEvent]: if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") @@ -2788,7 +2788,7 @@ async def submit_tool_outputs_and_poll( tool_outputs: Iterable[run_submit_tool_outputs_params.ToolOutput], run_id: str, thread_id: str, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 8e34210bd7..254a94435c 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -9,7 +9,7 @@ import httpx from ..... import _legacy_response -from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ....._utils import maybe_transform, async_maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource @@ -50,13 +50,13 @@ def retrieve( *, thread_id: str, run_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunStep: """ Retrieves a run step. @@ -103,17 +103,17 @@ def list( run_id: str, *, thread_id: str, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + include: List[RunStepInclude] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[RunStep]: """ Returns a list of run steps belonging to a run. @@ -206,13 +206,13 @@ async def retrieve( *, thread_id: str, run_id: str, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, + include: List[RunStepInclude] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunStep: """ Retrieves a run step. @@ -259,17 +259,17 @@ def list( run_id: str, *, thread_id: str, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - include: List[RunStepInclude] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + include: List[RunStepInclude] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[RunStep, AsyncCursorPage[RunStep]]: """ Returns a list of run steps belonging to a run. diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 7121851cab..681d3c2933 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -18,7 +18,7 @@ MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import required_args, maybe_transform, async_maybe_transform from .runs.runs import ( Runs, @@ -91,15 +91,15 @@ def with_streaming_response(self) -> ThreadsWithStreamingResponse: def create( self, *, - messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_params.ToolResources] | NotGiven = NOT_GIVEN, + messages: Iterable[thread_create_params.Message] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_create_params.ToolResources] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Thread: """ Create a thread. @@ -155,7 +155,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Thread: """ Retrieves a thread. @@ -185,14 +185,14 @@ def update( self, thread_id: str, *, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_update_params.ToolResources] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_update_params.ToolResources] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Thread: """ Modifies a thread. @@ -246,7 +246,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ThreadDeleted: """ Delete a thread. @@ -277,27 +277,27 @@ def create_and_run( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Create a thread and run it in one request. @@ -412,26 +412,26 @@ def create_and_run( *, assistant_id: str, stream: Literal[True], - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[AssistantStreamEvent]: """ Create a thread and run it in one request. @@ -546,26 +546,26 @@ def create_and_run( *, assistant_id: str, stream: bool, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | Stream[AssistantStreamEvent]: """ Create a thread and run it in one request. @@ -680,27 +680,27 @@ def create_and_run( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | Stream[AssistantStreamEvent]: extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( @@ -740,21 +740,21 @@ def create_and_run_poll( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -796,20 +796,20 @@ def create_and_run_stream( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -825,20 +825,20 @@ def create_and_run_stream( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, event_handler: AssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -854,20 +854,20 @@ def create_and_run_stream( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, event_handler: AssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -949,15 +949,15 @@ def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: async def create( self, *, - messages: Iterable[thread_create_params.Message] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_params.ToolResources] | NotGiven = NOT_GIVEN, + messages: Iterable[thread_create_params.Message] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_create_params.ToolResources] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Thread: """ Create a thread. @@ -1013,7 +1013,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Thread: """ Retrieves a thread. @@ -1043,14 +1043,14 @@ async def update( self, thread_id: str, *, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_update_params.ToolResources] | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + tool_resources: Optional[thread_update_params.ToolResources] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Thread: """ Modifies a thread. @@ -1104,7 +1104,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ThreadDeleted: """ Delete a thread. @@ -1135,27 +1135,27 @@ async def create_and_run( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run: """ Create a thread and run it in one request. @@ -1270,26 +1270,26 @@ async def create_and_run( *, assistant_id: str, stream: Literal[True], - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[AssistantStreamEvent]: """ Create a thread and run it in one request. @@ -1404,26 +1404,26 @@ async def create_and_run( *, assistant_id: str, stream: bool, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | AsyncStream[AssistantStreamEvent]: """ Create a thread and run it in one request. @@ -1538,27 +1538,27 @@ async def create_and_run( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Run | AsyncStream[AssistantStreamEvent]: extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( @@ -1598,21 +1598,21 @@ async def create_and_run_poll( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, + poll_interval_ms: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1656,20 +1656,20 @@ def create_and_run_stream( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1685,20 +1685,20 @@ def create_and_run_stream( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, event_handler: AsyncAssistantEventHandlerT, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1714,20 +1714,20 @@ def create_and_run_stream( self, *, assistant_id: str, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_prompt_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: Union[str, ChatModel, None] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - response_format: Optional[AssistantResponseFormatOptionParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN, - tool_choice: Optional[AssistantToolChoiceOptionParam] | NotGiven = NOT_GIVEN, - tool_resources: Optional[thread_create_and_run_params.ToolResources] | NotGiven = NOT_GIVEN, - tools: Optional[Iterable[AssistantToolParam]] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | NotGiven = NOT_GIVEN, + instructions: Optional[str] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_prompt_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: Union[str, ChatModel, None] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: Optional[AssistantResponseFormatOptionParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + thread: thread_create_and_run_params.Thread | Omit = omit, + tool_choice: Optional[AssistantToolChoiceOptionParam] | Omit = omit, + tool_resources: Optional[thread_create_and_run_params.ToolResources] | Omit = omit, + tools: Optional[Iterable[AssistantToolParam]] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation_strategy: Optional[thread_create_and_run_params.TruncationStrategy] | Omit = omit, event_handler: AsyncAssistantEventHandlerT | None = None, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index f29792a207..329634ba43 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -19,7 +19,7 @@ MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ...._utils import required_args, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -86,43 +86,43 @@ def parse( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - response_format: type[ResponseFormatT] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ParsedChatCompletion[ResponseFormatT]: """Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types & returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class. @@ -240,44 +240,44 @@ def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion: """ **Starting a new project?** We recommend trying @@ -529,43 +529,43 @@ def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: Literal[True], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[ChatCompletionChunk]: """ **Starting a new project?** We recommend trying @@ -817,43 +817,43 @@ def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: bool, - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion | Stream[ChatCompletionChunk]: """ **Starting a new project?** We recommend trying @@ -1104,44 +1104,44 @@ def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion | Stream[ChatCompletionChunk]: validate_response_format(response_format) return self._post( @@ -1204,7 +1204,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion: """Get a stored chat completion. @@ -1240,7 +1240,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion: """Modify a stored chat completion. @@ -1278,17 +1278,17 @@ def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: str | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: str | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[ChatCompletion]: """List stored Chat Completions. @@ -1351,7 +1351,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletionDeleted: """Delete a stored chat completion. @@ -1382,43 +1382,43 @@ def stream( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletionStreamManager[ResponseFormatT]: """Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API and automatic accumulation of each delta. @@ -1524,43 +1524,43 @@ async def parse( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - response_format: type[ResponseFormatT] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ParsedChatCompletion[ResponseFormatT]: """Wrapper over the `client.chat.completions.create()` method that provides richer integrations with Python specific types & returns a `ParsedChatCompletion` object, which is a subclass of the standard `ChatCompletion` class. @@ -1678,44 +1678,44 @@ async def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion: """ **Starting a new project?** We recommend trying @@ -1967,43 +1967,43 @@ async def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: Literal[True], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[ChatCompletionChunk]: """ **Starting a new project?** We recommend trying @@ -2255,43 +2255,43 @@ async def create( messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], stream: bool, - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: """ **Starting a new project?** We recommend trying @@ -2542,44 +2542,44 @@ async def create( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]: validate_response_format(response_format) return await self._post( @@ -2642,7 +2642,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion: """Get a stored chat completion. @@ -2678,7 +2678,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletion: """Modify a stored chat completion. @@ -2716,17 +2716,17 @@ async def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: str | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: str | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ChatCompletion, AsyncCursorPage[ChatCompletion]]: """List stored Chat Completions. @@ -2789,7 +2789,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatCompletionDeleted: """Delete a stored chat completion. @@ -2820,43 +2820,43 @@ def stream( *, messages: Iterable[ChatCompletionMessageParam], model: Union[str, ChatModel], - audio: Optional[ChatCompletionAudioParam] | NotGiven = NOT_GIVEN, - response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - function_call: completion_create_params.FunctionCall | NotGiven = NOT_GIVEN, - functions: Iterable[completion_create_params.Function] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[bool] | NotGiven = NOT_GIVEN, - max_completion_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - modalities: Optional[List[Literal["text", "audio"]]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - parallel_tool_calls: bool | NotGiven = NOT_GIVEN, - prediction: Optional[ChatCompletionPredictionContentParam] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning_effort: Optional[ReasoningEffort] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN, - tools: Iterable[ChatCompletionToolUnionParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, - web_search_options: completion_create_params.WebSearchOptions | NotGiven = NOT_GIVEN, + audio: Optional[ChatCompletionAudioParam] | Omit = omit, + response_format: completion_create_params.ResponseFormat | type[ResponseFormatT] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + function_call: completion_create_params.FunctionCall | Omit = omit, + functions: Iterable[completion_create_params.Function] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[bool] | Omit = omit, + max_completion_tokens: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + n: Optional[int] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning_effort: Optional[ReasoningEffort] | Omit = omit, + safety_identifier: str | Omit = omit, + seed: Optional[int] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + temperature: Optional[float] | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, + web_search_options: completion_create_params.WebSearchOptions | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncChatCompletionStreamManager[ResponseFormatT]: """Wrapper over the `client.chat.completions.create(stream=True)` method that provides a more granular event API and automatic accumulation of each delta. diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py index fac15fba8b..3d6dc79cd6 100644 --- a/src/openai/resources/chat/completions/messages.py +++ b/src/openai/resources/chat/completions/messages.py @@ -7,7 +7,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -44,15 +44,15 @@ def list( self, completion_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[ChatCompletionStoreMessage]: """Get the messages in a stored chat completion. @@ -122,15 +122,15 @@ def list( self, completion_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ChatCompletionStoreMessage, AsyncCursorPage[ChatCompletionStoreMessage]]: """Get the messages in a stored chat completion. diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 97a84575ab..2f2284a622 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -9,7 +9,7 @@ from .. import _legacy_response from ..types import completion_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import required_args, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -50,28 +50,28 @@ def create( *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion: """ Creates a completion for the provided prompt and parameters. @@ -206,27 +206,27 @@ def create( model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: Literal[True], - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -361,27 +361,27 @@ def create( model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: bool, - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | Stream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -515,28 +515,28 @@ def create( *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | Stream[Completion]: return self._post( "/completions", @@ -600,28 +600,28 @@ async def create( *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion: """ Creates a completion for the provided prompt and parameters. @@ -756,27 +756,27 @@ async def create( model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: Literal[True], - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -911,27 +911,27 @@ async def create( model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], stream: bool, - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | AsyncStream[Completion]: """ Creates a completion for the provided prompt and parameters. @@ -1065,28 +1065,28 @@ async def create( *, model: Union[str, Literal["gpt-3.5-turbo-instruct", "davinci-002", "babbage-002"]], prompt: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]], None], - best_of: Optional[int] | NotGiven = NOT_GIVEN, - echo: Optional[bool] | NotGiven = NOT_GIVEN, - frequency_penalty: Optional[float] | NotGiven = NOT_GIVEN, - logit_bias: Optional[Dict[str, int]] | NotGiven = NOT_GIVEN, - logprobs: Optional[int] | NotGiven = NOT_GIVEN, - max_tokens: Optional[int] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - presence_penalty: Optional[float] | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - stop: Union[Optional[str], SequenceNotStr[str], None] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[ChatCompletionStreamOptionsParam] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + best_of: Optional[int] | Omit = omit, + echo: Optional[bool] | Omit = omit, + frequency_penalty: Optional[float] | Omit = omit, + logit_bias: Optional[Dict[str, int]] | Omit = omit, + logprobs: Optional[int] | Omit = omit, + max_tokens: Optional[int] | Omit = omit, + n: Optional[int] | Omit = omit, + presence_penalty: Optional[float] | Omit = omit, + seed: Optional[int] | Omit = omit, + stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[ChatCompletionStreamOptionsParam] | Omit = omit, + suffix: Optional[str] | Omit = omit, + temperature: Optional[float] | Omit = omit, + top_p: Optional[float] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | AsyncStream[Completion]: return await self._post( "/completions", diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py index 30e9eff127..dcdc3e1a3e 100644 --- a/src/openai/resources/containers/containers.py +++ b/src/openai/resources/containers/containers.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ...types import container_list_params, container_create_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, SequenceNotStr +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -58,14 +58,14 @@ def create( self, *, name: str, - expires_after: container_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + expires_after: container_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ContainerCreateResponse: """ Create Container @@ -110,7 +110,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ContainerRetrieveResponse: """ Retrieve Container @@ -137,15 +137,15 @@ def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[ContainerListResponse]: """List Containers @@ -200,7 +200,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete Container @@ -254,14 +254,14 @@ async def create( self, *, name: str, - expires_after: container_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, + expires_after: container_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ContainerCreateResponse: """ Create Container @@ -306,7 +306,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ContainerRetrieveResponse: """ Retrieve Container @@ -333,15 +333,15 @@ async def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ContainerListResponse, AsyncCursorPage[ContainerListResponse]]: """List Containers @@ -396,7 +396,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete Container diff --git a/src/openai/resources/containers/files/content.py b/src/openai/resources/containers/files/content.py index a200383407..a3dbd0e8c7 100644 --- a/src/openai/resources/containers/files/content.py +++ b/src/openai/resources/containers/files/content.py @@ -5,7 +5,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Query, Headers, NotGiven, not_given from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -49,7 +49,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ Retrieve Container File Content @@ -107,7 +107,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ Retrieve Container File Content diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py index 624398b97b..a472cfc9f3 100644 --- a/src/openai/resources/containers/files/files.py +++ b/src/openai/resources/containers/files/files.py @@ -16,7 +16,7 @@ ContentWithStreamingResponse, AsyncContentWithStreamingResponse, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven, FileTypes +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -59,14 +59,14 @@ def create( self, container_id: str, *, - file: FileTypes | NotGiven = NOT_GIVEN, - file_id: str | NotGiven = NOT_GIVEN, + file: FileTypes | Omit = omit, + file_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileCreateResponse: """ Create a Container File @@ -120,7 +120,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileRetrieveResponse: """ Retrieve Container File @@ -150,15 +150,15 @@ def list( self, container_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[FileListResponse]: """List Container files @@ -216,7 +216,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete Container File @@ -272,14 +272,14 @@ async def create( self, container_id: str, *, - file: FileTypes | NotGiven = NOT_GIVEN, - file_id: str | NotGiven = NOT_GIVEN, + file: FileTypes | Omit = omit, + file_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileCreateResponse: """ Create a Container File @@ -333,7 +333,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileRetrieveResponse: """ Retrieve Container File @@ -363,15 +363,15 @@ def list( self, container_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[FileListResponse, AsyncCursorPage[FileListResponse]]: """List Container files @@ -429,7 +429,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Delete Container File diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py index c0239d402c..4b942eb014 100644 --- a/src/openai/resources/conversations/conversations.py +++ b/src/openai/resources/conversations/conversations.py @@ -15,7 +15,7 @@ ItemsWithStreamingResponse, AsyncItemsWithStreamingResponse, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -57,14 +57,14 @@ def with_streaming_response(self) -> ConversationsWithStreamingResponse: def create( self, *, - items: Optional[Iterable[ResponseInputItemParam]] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + items: Optional[Iterable[ResponseInputItemParam]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Create a conversation. @@ -112,7 +112,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Get a conversation with the given ID. @@ -146,7 +146,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Update a conversation's metadata with the given ID. @@ -186,7 +186,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationDeletedResource: """ Delete a conversation with the given ID. @@ -238,14 +238,14 @@ def with_streaming_response(self) -> AsyncConversationsWithStreamingResponse: async def create( self, *, - items: Optional[Iterable[ResponseInputItemParam]] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, + items: Optional[Iterable[ResponseInputItemParam]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Create a conversation. @@ -293,7 +293,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Get a conversation with the given ID. @@ -327,7 +327,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Update a conversation's metadata with the given ID. @@ -369,7 +369,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationDeletedResource: """ Delete a conversation with the given ID. diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py index 01811f956b..3dba144849 100644 --- a/src/openai/resources/conversations/items.py +++ b/src/openai/resources/conversations/items.py @@ -8,7 +8,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -50,13 +50,13 @@ def create( conversation_id: str, *, items: Iterable[ResponseInputItemParam], - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationItemList: """ Create items in a conversation with the given ID. @@ -96,13 +96,13 @@ def retrieve( item_id: str, *, conversation_id: str, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationItem: """ Get a single item from a conversation with the given IDs. @@ -143,16 +143,16 @@ def list( self, conversation_id: str, *, - after: str | NotGiven = NOT_GIVEN, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncConversationCursorPage[ConversationItem]: """ List all items for a conversation with the given ID. @@ -228,7 +228,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Delete an item from a conversation with the given IDs. @@ -280,13 +280,13 @@ async def create( conversation_id: str, *, items: Iterable[ResponseInputItemParam], - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationItemList: """ Create items in a conversation with the given ID. @@ -326,13 +326,13 @@ async def retrieve( item_id: str, *, conversation_id: str, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationItem: """ Get a single item from a conversation with the given IDs. @@ -373,16 +373,16 @@ def list( self, conversation_id: str, *, - after: str | NotGiven = NOT_GIVEN, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ConversationItem, AsyncConversationCursorPage[ConversationItem]]: """ List all items for a conversation with the given ID. @@ -458,7 +458,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ Delete an item from a conversation with the given IDs. diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index a8cf179850..5dc3dfa9b3 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -11,7 +11,7 @@ from .. import _legacy_response from ..types import embedding_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import is_given, maybe_transform from .._compat import cached_property from .._extras import numpy as np, has_numpy @@ -49,15 +49,15 @@ def create( *, input: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]], model: Union[str, EmbeddingModel], - dimensions: int | NotGiven = NOT_GIVEN, - encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + dimensions: int | Omit = omit, + encoding_format: Literal["float", "base64"] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CreateEmbeddingResponse: """ Creates an embedding vector representing the input text. @@ -168,15 +168,15 @@ async def create( *, input: Union[str, SequenceNotStr[str], Iterable[int], Iterable[Iterable[int]]], model: Union[str, EmbeddingModel], - dimensions: int | NotGiven = NOT_GIVEN, - encoding_format: Literal["float", "base64"] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + dimensions: int | Omit = omit, + encoding_format: Literal["float", "base64"] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CreateEmbeddingResponse: """ Creates an embedding vector representing the input text. diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py index 7aba192c51..40c4a3e9a3 100644 --- a/src/openai/resources/evals/evals.py +++ b/src/openai/resources/evals/evals.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ...types import eval_list_params, eval_create_params, eval_update_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from .runs.runs import ( @@ -63,14 +63,14 @@ def create( *, data_source_config: eval_create_params.DataSourceConfig, testing_criteria: Iterable[eval_create_params.TestingCriterion], - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalCreateResponse: """ Create the structure of an evaluation that can be used to test a model's @@ -132,7 +132,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalRetrieveResponse: """ Get an evaluation by ID. @@ -160,14 +160,14 @@ def update( self, eval_id: str, *, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalUpdateResponse: """ Update certain properties of an evaluation. @@ -210,16 +210,16 @@ def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - order_by: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + order_by: Literal["created_at", "updated_at"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[EvalListResponse]: """ List evaluations for a project. @@ -273,7 +273,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalDeleteResponse: """ Delete an evaluation. @@ -327,14 +327,14 @@ async def create( *, data_source_config: eval_create_params.DataSourceConfig, testing_criteria: Iterable[eval_create_params.TestingCriterion], - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalCreateResponse: """ Create the structure of an evaluation that can be used to test a model's @@ -396,7 +396,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalRetrieveResponse: """ Get an evaluation by ID. @@ -424,14 +424,14 @@ async def update( self, eval_id: str, *, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalUpdateResponse: """ Update certain properties of an evaluation. @@ -474,16 +474,16 @@ async def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - order_by: Literal["created_at", "updated_at"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + order_by: Literal["created_at", "updated_at"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[EvalListResponse, AsyncCursorPage[EvalListResponse]]: """ List evaluations for a project. @@ -537,7 +537,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> EvalDeleteResponse: """ Delete an evaluation. diff --git a/src/openai/resources/evals/runs/output_items.py b/src/openai/resources/evals/runs/output_items.py index 8fd0fdea92..c2dee72122 100644 --- a/src/openai/resources/evals/runs/output_items.py +++ b/src/openai/resources/evals/runs/output_items.py @@ -7,7 +7,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -52,7 +52,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OutputItemRetrieveResponse: """ Get an evaluation run output item by ID. @@ -85,16 +85,16 @@ def list( run_id: str, *, eval_id: str, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - status: Literal["fail", "pass"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["fail", "pass"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[OutputItemListResponse]: """ Get a list of output items for an evaluation run. @@ -175,7 +175,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> OutputItemRetrieveResponse: """ Get an evaluation run output item by ID. @@ -208,16 +208,16 @@ def list( run_id: str, *, eval_id: str, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - status: Literal["fail", "pass"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["fail", "pass"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[OutputItemListResponse, AsyncCursorPage[OutputItemListResponse]]: """ Get a list of output items for an evaluation run. diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py index 7efc61292c..b747b198f8 100644 --- a/src/openai/resources/evals/runs/runs.py +++ b/src/openai/resources/evals/runs/runs.py @@ -8,7 +8,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -63,14 +63,14 @@ def create( eval_id: str, *, data_source: run_create_params.DataSource, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunCreateResponse: """ Kicks off a new run for a given evaluation, specifying the data source, and what @@ -125,7 +125,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunRetrieveResponse: """ Get an evaluation run by ID. @@ -155,16 +155,16 @@ def list( self, eval_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[RunListResponse]: """ Get a list of runs for an evaluation. @@ -221,7 +221,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunDeleteResponse: """ Delete an eval run. @@ -257,7 +257,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunCancelResponse: """ Cancel an ongoing evaluation run. @@ -313,14 +313,14 @@ async def create( eval_id: str, *, data_source: run_create_params.DataSource, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunCreateResponse: """ Kicks off a new run for a given evaluation, specifying the data source, and what @@ -375,7 +375,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunRetrieveResponse: """ Get an evaluation run by ID. @@ -405,16 +405,16 @@ def list( self, eval_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[RunListResponse, AsyncCursorPage[RunListResponse]]: """ Get a list of runs for an evaluation. @@ -471,7 +471,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunDeleteResponse: """ Delete an eval run. @@ -507,7 +507,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> RunCancelResponse: """ Cancel an ongoing evaluation run. diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 963c3c0a9f..77bb2d613c 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -11,7 +11,7 @@ from .. import _legacy_response from ..types import FilePurpose, file_list_params, file_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -57,13 +57,13 @@ def create( *, file: FileTypes, purpose: FilePurpose, - expires_after: file_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + expires_after: file_create_params.ExpiresAfter | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileObject: """Upload a file that can be used across various endpoints. @@ -139,7 +139,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileObject: """ Returns information about a specific file. @@ -166,16 +166,16 @@ def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - purpose: str | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + purpose: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[FileObject]: """Returns a list of files. @@ -233,7 +233,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileDeleted: """ Delete a file. @@ -266,7 +266,7 @@ def content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -301,7 +301,7 @@ def retrieve_content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: """ Returns the contents of the specified file. @@ -374,13 +374,13 @@ async def create( *, file: FileTypes, purpose: FilePurpose, - expires_after: file_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + expires_after: file_create_params.ExpiresAfter | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileObject: """Upload a file that can be used across various endpoints. @@ -456,7 +456,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileObject: """ Returns information about a specific file. @@ -483,16 +483,16 @@ async def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, - purpose: str | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + purpose: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[FileObject, AsyncCursorPage[FileObject]]: """Returns a list of files. @@ -550,7 +550,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileDeleted: """ Delete a file. @@ -583,7 +583,7 @@ async def content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -618,7 +618,7 @@ async def retrieve_content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: """ Returns the contents of the specified file. diff --git a/src/openai/resources/fine_tuning/alpha/graders.py b/src/openai/resources/fine_tuning/alpha/graders.py index 387e6c72ff..e7a9b925ea 100644 --- a/src/openai/resources/fine_tuning/alpha/graders.py +++ b/src/openai/resources/fine_tuning/alpha/graders.py @@ -5,7 +5,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -43,13 +43,13 @@ def run( *, grader: grader_run_params.Grader, model_sample: str, - item: object | NotGiven = NOT_GIVEN, + item: object | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> GraderRunResponse: """ Run a grader. @@ -100,7 +100,7 @@ def validate( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> GraderValidateResponse: """ Validate a grader. @@ -151,13 +151,13 @@ async def run( *, grader: grader_run_params.Grader, model_sample: str, - item: object | NotGiven = NOT_GIVEN, + item: object | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> GraderRunResponse: """ Run a grader. @@ -208,7 +208,7 @@ async def validate( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> GraderValidateResponse: """ Validate a grader. diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py index f8ae125941..e7f55b82d9 100644 --- a/src/openai/resources/fine_tuning/checkpoints/permissions.py +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -7,7 +7,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -52,7 +52,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[PermissionCreateResponse]: """ **NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys). @@ -90,16 +90,16 @@ def retrieve( self, fine_tuned_model_checkpoint: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["ascending", "descending"] | NotGiven = NOT_GIVEN, - project_id: str | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PermissionRetrieveResponse: """ **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). @@ -158,7 +158,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PermissionDeleteResponse: """ **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). @@ -220,7 +220,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[PermissionCreateResponse, AsyncPage[PermissionCreateResponse]]: """ **NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys). @@ -258,16 +258,16 @@ async def retrieve( self, fine_tuned_model_checkpoint: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["ascending", "descending"] | NotGiven = NOT_GIVEN, - project_id: str | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PermissionRetrieveResponse: """ **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). @@ -326,7 +326,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PermissionDeleteResponse: """ **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py index f86462e513..f65856f0c6 100644 --- a/src/openai/resources/fine_tuning/jobs/checkpoints.py +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -5,7 +5,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -45,14 +45,14 @@ def list( self, fine_tuning_job_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[FineTuningJobCheckpoint]: """ List checkpoints for a fine-tuning job. @@ -116,14 +116,14 @@ def list( self, fine_tuning_job_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[FineTuningJobCheckpoint, AsyncCursorPage[FineTuningJobCheckpoint]]: """ List checkpoints for a fine-tuning job. diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index ee21cdd280..b292e057cf 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -8,7 +8,7 @@ import httpx from .... import _legacy_response -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from .checkpoints import ( @@ -63,19 +63,19 @@ def create( *, model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]], training_file: str, - hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, - integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - method: job_create_params.Method | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - validation_file: Optional[str] | NotGiven = NOT_GIVEN, + hyperparameters: job_create_params.Hyperparameters | Omit = omit, + integrations: Optional[Iterable[job_create_params.Integration]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + method: job_create_params.Method | Omit = omit, + seed: Optional[int] | Omit = omit, + suffix: Optional[str] | Omit = omit, + validation_file: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Creates a fine-tuning job which begins the process of creating a new model from @@ -186,7 +186,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Get info about a fine-tuning job. @@ -215,15 +215,15 @@ def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[FineTuningJob]: """ List your organization's fine-tuning jobs @@ -273,7 +273,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Immediately cancel a fine-tune job. @@ -301,14 +301,14 @@ def list_events( self, fine_tuning_job_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[FineTuningJobEvent]: """ Get status updates for a fine-tuning job. @@ -356,7 +356,7 @@ def pause( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Pause a fine-tune job. @@ -389,7 +389,7 @@ def resume( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Resume a fine-tune job. @@ -443,19 +443,19 @@ async def create( *, model: Union[str, Literal["babbage-002", "davinci-002", "gpt-3.5-turbo", "gpt-4o-mini"]], training_file: str, - hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN, - integrations: Optional[Iterable[job_create_params.Integration]] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - method: job_create_params.Method | NotGiven = NOT_GIVEN, - seed: Optional[int] | NotGiven = NOT_GIVEN, - suffix: Optional[str] | NotGiven = NOT_GIVEN, - validation_file: Optional[str] | NotGiven = NOT_GIVEN, + hyperparameters: job_create_params.Hyperparameters | Omit = omit, + integrations: Optional[Iterable[job_create_params.Integration]] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + method: job_create_params.Method | Omit = omit, + seed: Optional[int] | Omit = omit, + suffix: Optional[str] | Omit = omit, + validation_file: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Creates a fine-tuning job which begins the process of creating a new model from @@ -566,7 +566,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Get info about a fine-tuning job. @@ -595,15 +595,15 @@ async def retrieve( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[FineTuningJob, AsyncCursorPage[FineTuningJob]]: """ List your organization's fine-tuning jobs @@ -653,7 +653,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Immediately cancel a fine-tune job. @@ -681,14 +681,14 @@ def list_events( self, fine_tuning_job_id: str, *, - after: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[FineTuningJobEvent, AsyncCursorPage[FineTuningJobEvent]]: """ Get status updates for a fine-tuning job. @@ -736,7 +736,7 @@ async def pause( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Pause a fine-tune job. @@ -769,7 +769,7 @@ async def resume( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FineTuningJob: """ Resume a fine-tune job. diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 17ec264b6a..aae26bab64 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -9,7 +9,7 @@ from .. import _legacy_response from ..types import image_edit_params, image_generate_params, image_create_variation_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given from .._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -48,17 +48,17 @@ def create_variation( self, *, image: FileTypes, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse: """Creates a variation of a given image. @@ -123,26 +123,25 @@ def edit( *, image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse: """Creates an edited or extended image given one or more source images and a prompt. @@ -237,25 +236,24 @@ def edit( image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: Literal[True], - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[ImageEditStreamEvent]: """Creates an edited or extended image given one or more source images and a prompt. @@ -350,25 +348,24 @@ def edit( image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: bool, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | Stream[ImageEditStreamEvent]: """Creates an edited or extended image given one or more source images and a prompt. @@ -462,26 +459,25 @@ def edit( *, image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | Stream[ImageEditStreamEvent]: body = deepcopy_minimal( { @@ -527,28 +523,28 @@ def generate( self, *, prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse: """ Creates an image given a prompt. @@ -638,27 +634,27 @@ def generate( *, prompt: str, stream: Literal[True], - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[ImageGenStreamEvent]: """ Creates an image given a prompt. @@ -748,27 +744,27 @@ def generate( *, prompt: str, stream: bool, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | Stream[ImageGenStreamEvent]: """ Creates an image given a prompt. @@ -857,28 +853,28 @@ def generate( self, *, prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | Stream[ImageGenStreamEvent]: return self._post( "/images/generations", @@ -936,17 +932,17 @@ async def create_variation( self, *, image: FileTypes, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse: """Creates a variation of a given image. @@ -1011,26 +1007,25 @@ async def edit( *, image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse: """Creates an edited or extended image given one or more source images and a prompt. @@ -1125,25 +1120,24 @@ async def edit( image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: Literal[True], - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[ImageEditStreamEvent]: """Creates an edited or extended image given one or more source images and a prompt. @@ -1238,25 +1232,24 @@ async def edit( image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, stream: bool, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]: """Creates an edited or extended image given one or more source images and a prompt. @@ -1350,26 +1343,25 @@ async def edit( *, image: Union[FileTypes, SequenceNotStr[FileTypes]], prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - input_fidelity: Optional[Literal["high", "low"]] | NotGiven = NOT_GIVEN, - mask: FileTypes | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + input_fidelity: Optional[Literal["high", "low"]] | Omit = omit, + mask: FileTypes | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, + size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]: body = deepcopy_minimal( { @@ -1415,28 +1407,28 @@ async def generate( self, *, prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse: """ Creates an image given a prompt. @@ -1526,27 +1518,27 @@ async def generate( *, prompt: str, stream: Literal[True], - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[ImageGenStreamEvent]: """ Creates an image given a prompt. @@ -1636,27 +1628,27 @@ async def generate( *, prompt: str, stream: bool, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]: """ Creates an image given a prompt. @@ -1745,28 +1737,28 @@ async def generate( self, *, prompt: str, - background: Optional[Literal["transparent", "opaque", "auto"]] | NotGiven = NOT_GIVEN, - model: Union[str, ImageModel, None] | NotGiven = NOT_GIVEN, - moderation: Optional[Literal["low", "auto"]] | NotGiven = NOT_GIVEN, - n: Optional[int] | NotGiven = NOT_GIVEN, - output_compression: Optional[int] | NotGiven = NOT_GIVEN, - output_format: Optional[Literal["png", "jpeg", "webp"]] | NotGiven = NOT_GIVEN, - partial_images: Optional[int] | NotGiven = NOT_GIVEN, - quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | NotGiven = NOT_GIVEN, - response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN, + background: Optional[Literal["transparent", "opaque", "auto"]] | Omit = omit, + model: Union[str, ImageModel, None] | Omit = omit, + moderation: Optional[Literal["low", "auto"]] | Omit = omit, + n: Optional[int] | Omit = omit, + output_compression: Optional[int] | Omit = omit, + output_format: Optional[Literal["png", "jpeg", "webp"]] | Omit = omit, + partial_images: Optional[int] | Omit = omit, + quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, + response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, size: Optional[ Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] ] - | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + style: Optional[Literal["vivid", "natural"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | AsyncStream[ImageGenStreamEvent]: return await self._post( "/images/generations", diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index a9693a6b0a..a8f7691055 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -5,7 +5,7 @@ import httpx from .. import _legacy_response -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -49,7 +49,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Model: """ Retrieves a model instance, providing basic information about the model such as @@ -82,7 +82,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[Model]: """ Lists the currently available models, and provides basic information about each @@ -106,7 +106,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModelDeleted: """Delete a fine-tuned model. @@ -162,7 +162,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Model: """ Retrieves a model instance, providing basic information about the model such as @@ -195,7 +195,7 @@ def list( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Model, AsyncPage[Model]]: """ Lists the currently available models, and provides basic information about each @@ -219,7 +219,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModelDeleted: """Delete a fine-tuned model. diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 91c0df4358..5f378f71e7 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -8,7 +8,7 @@ from .. import _legacy_response from ..types import moderation_create_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -45,13 +45,13 @@ def create( self, *, input: Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]], - model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, + model: Union[str, ModerationModel] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModerationCreateResponse: """Classifies if text and/or image inputs are potentially harmful. @@ -115,13 +115,13 @@ async def create( self, *, input: Union[str, SequenceNotStr[str], Iterable[ModerationMultiModalInputParam]], - model: Union[str, ModerationModel] | NotGiven = NOT_GIVEN, + model: Union[str, ModerationModel] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModerationCreateResponse: """Classifies if text and/or image inputs are potentially harmful. diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py index a79460746d..5ceba7bef1 100644 --- a/src/openai/resources/realtime/client_secrets.py +++ b/src/openai/resources/realtime/client_secrets.py @@ -5,7 +5,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -40,14 +40,14 @@ def with_streaming_response(self) -> ClientSecretsWithStreamingResponse: def create( self, *, - expires_after: client_secret_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - session: client_secret_create_params.Session | NotGiven = NOT_GIVEN, + expires_after: client_secret_create_params.ExpiresAfter | Omit = omit, + session: client_secret_create_params.Session | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClientSecretCreateResponse: """ Create a Realtime client secret with an associated session configuration. @@ -108,14 +108,14 @@ def with_streaming_response(self) -> AsyncClientSecretsWithStreamingResponse: async def create( self, *, - expires_after: client_secret_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - session: client_secret_create_params.Session | NotGiven = NOT_GIVEN, + expires_after: client_secret_create_params.ExpiresAfter | Omit = omit, + session: client_secret_create_params.Session | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ClientSecretCreateResponse: """ Create a Realtime client secret with an associated session configuration. diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 64fca72915..9d61fa25e0 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -11,7 +11,7 @@ import httpx from pydantic import BaseModel -from ..._types import NOT_GIVEN, Query, Headers, NotGiven +from ..._types import Omit, Query, Headers, omit from ..._utils import ( is_azure_client, maybe_transform, @@ -557,7 +557,7 @@ def __init__(self, connection: RealtimeConnection) -> None: class RealtimeSessionResource(BaseRealtimeConnectionResource): - def update(self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN) -> None: + def update(self, *, session: session_update_event_param.Session, event_id: str | Omit = omit) -> None: """ Send this event to update the session’s configuration. The client may send this event at any time to update any field @@ -578,12 +578,7 @@ def update(self, *, session: session_update_event_param.Session, event_id: str | class RealtimeResponseResource(BaseRealtimeConnectionResource): - def create( - self, - *, - event_id: str | NotGiven = NOT_GIVEN, - response: RealtimeResponseCreateParamsParam | NotGiven = NOT_GIVEN, - ) -> None: + def create(self, *, event_id: str | Omit = omit, response: RealtimeResponseCreateParamsParam | Omit = omit) -> None: """ This event instructs the server to create a Response, which means triggering model inference. When in Server VAD mode, the server will create Responses @@ -618,7 +613,7 @@ def create( ) ) - def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + def cancel(self, *, event_id: str | Omit = omit, response_id: str | Omit = omit) -> None: """Send this event to cancel an in-progress response. The server will respond @@ -636,7 +631,7 @@ def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | Not class RealtimeInputAudioBufferResource(BaseRealtimeConnectionResource): - def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + def clear(self, *, event_id: str | Omit = omit) -> None: """Send this event to clear the audio bytes in the buffer. The server will @@ -646,7 +641,7 @@ def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) ) - def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + def commit(self, *, event_id: str | Omit = omit) -> None: """ Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. @@ -656,7 +651,7 @@ def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) ) - def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + def append(self, *, audio: str, event_id: str | Omit = omit) -> None: """Send this event to append audio bytes to the input audio buffer. The audio @@ -688,7 +683,7 @@ def item(self) -> RealtimeConversationItemResource: class RealtimeConversationItemResource(BaseRealtimeConnectionResource): - def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + def delete(self, *, item_id: str, event_id: str | Omit = omit) -> None: """Send this event when you want to remove any item from the conversation history. @@ -704,11 +699,7 @@ def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: ) def create( - self, - *, - item: ConversationItemParam, - event_id: str | NotGiven = NOT_GIVEN, - previous_item_id: str | NotGiven = NOT_GIVEN, + self, *, item: ConversationItemParam, event_id: str | Omit = omit, previous_item_id: str | Omit = omit ) -> None: """ Add a new Item to the Conversation's context, including messages, function @@ -733,9 +724,7 @@ def create( ) ) - def truncate( - self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN - ) -> None: + def truncate(self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | Omit = omit) -> None: """Send this event to truncate a previous assistant message’s audio. The server @@ -765,7 +754,7 @@ def truncate( ) ) - def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + def retrieve(self, *, item_id: str, event_id: str | Omit = omit) -> None: """ Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. The server will respond with a `conversation.item.retrieved` event, @@ -781,7 +770,7 @@ def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> Non class RealtimeOutputAudioBufferResource(BaseRealtimeConnectionResource): - def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + def clear(self, *, event_id: str | Omit = omit) -> None: """**WebRTC Only:** Emit to cut off the current audio response. This will trigger the server to @@ -801,9 +790,7 @@ def __init__(self, connection: AsyncRealtimeConnection) -> None: class AsyncRealtimeSessionResource(BaseAsyncRealtimeConnectionResource): - async def update( - self, *, session: session_update_event_param.Session, event_id: str | NotGiven = NOT_GIVEN - ) -> None: + async def update(self, *, session: session_update_event_param.Session, event_id: str | Omit = omit) -> None: """ Send this event to update the session’s configuration. The client may send this event at any time to update any field @@ -825,10 +812,7 @@ async def update( class AsyncRealtimeResponseResource(BaseAsyncRealtimeConnectionResource): async def create( - self, - *, - event_id: str | NotGiven = NOT_GIVEN, - response: RealtimeResponseCreateParamsParam | NotGiven = NOT_GIVEN, + self, *, event_id: str | Omit = omit, response: RealtimeResponseCreateParamsParam | Omit = omit ) -> None: """ This event instructs the server to create a Response, which means triggering @@ -864,7 +848,7 @@ async def create( ) ) - async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str | NotGiven = NOT_GIVEN) -> None: + async def cancel(self, *, event_id: str | Omit = omit, response_id: str | Omit = omit) -> None: """Send this event to cancel an in-progress response. The server will respond @@ -882,7 +866,7 @@ async def cancel(self, *, event_id: str | NotGiven = NOT_GIVEN, response_id: str class AsyncRealtimeInputAudioBufferResource(BaseAsyncRealtimeConnectionResource): - async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def clear(self, *, event_id: str | Omit = omit) -> None: """Send this event to clear the audio bytes in the buffer. The server will @@ -892,7 +876,7 @@ async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.clear", "event_id": event_id})) ) - async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def commit(self, *, event_id: str | Omit = omit) -> None: """ Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. @@ -902,7 +886,7 @@ async def commit(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: cast(RealtimeClientEventParam, strip_not_given({"type": "input_audio_buffer.commit", "event_id": event_id})) ) - async def append(self, *, audio: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def append(self, *, audio: str, event_id: str | Omit = omit) -> None: """Send this event to append audio bytes to the input audio buffer. The audio @@ -934,7 +918,7 @@ def item(self) -> AsyncRealtimeConversationItemResource: class AsyncRealtimeConversationItemResource(BaseAsyncRealtimeConnectionResource): - async def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def delete(self, *, item_id: str, event_id: str | Omit = omit) -> None: """Send this event when you want to remove any item from the conversation history. @@ -950,11 +934,7 @@ async def delete(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> ) async def create( - self, - *, - item: ConversationItemParam, - event_id: str | NotGiven = NOT_GIVEN, - previous_item_id: str | NotGiven = NOT_GIVEN, + self, *, item: ConversationItemParam, event_id: str | Omit = omit, previous_item_id: str | Omit = omit ) -> None: """ Add a new Item to the Conversation's context, including messages, function @@ -980,7 +960,7 @@ async def create( ) async def truncate( - self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | NotGiven = NOT_GIVEN + self, *, audio_end_ms: int, content_index: int, item_id: str, event_id: str | Omit = omit ) -> None: """Send this event to truncate a previous assistant message’s audio. @@ -1011,7 +991,7 @@ async def truncate( ) ) - async def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def retrieve(self, *, item_id: str, event_id: str | Omit = omit) -> None: """ Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. The server will respond with a `conversation.item.retrieved` event, @@ -1027,7 +1007,7 @@ async def retrieve(self, *, item_id: str, event_id: str | NotGiven = NOT_GIVEN) class AsyncRealtimeOutputAudioBufferResource(BaseAsyncRealtimeConnectionResource): - async def clear(self, *, event_id: str | NotGiven = NOT_GIVEN) -> None: + async def clear(self, *, event_id: str | Omit = omit) -> None: """**WebRTC Only:** Emit to cut off the current audio response. This will trigger the server to diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py index 9f3ef637ce..3311bfe10a 100644 --- a/src/openai/resources/responses/input_items.py +++ b/src/openai/resources/responses/input_items.py @@ -8,7 +8,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -46,16 +46,16 @@ def list( self, response_id: str, *, - after: str | NotGiven = NOT_GIVEN, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[ResponseItem]: """ Returns a list of input items for a given response. @@ -130,16 +130,16 @@ def list( self, response_id: str, *, - after: str | NotGiven = NOT_GIVEN, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + include: List[ResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ResponseItem, AsyncCursorPage[ResponseItem]]: """ Returns a list of input items for a given response. diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 8acdb10b51..0a89d0c18e 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -9,7 +9,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NoneType, NotGiven +from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from ..._utils import is_given, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -75,39 +75,39 @@ def with_streaming_response(self) -> ResponsesWithStreamingResponse: def create( self, *, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response: """Creates a model response. @@ -315,38 +315,38 @@ def create( self, *, stream: Literal[True], - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[ResponseStreamEvent]: """Creates a model response. @@ -554,38 +554,38 @@ def create( self, *, stream: bool, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | Stream[ResponseStreamEvent]: """Creates a model response. @@ -791,39 +791,39 @@ def create( def create( self, *, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | Stream[ResponseStreamEvent]: return self._post( "/responses", @@ -874,9 +874,9 @@ def stream( self, *, response_id: str, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + text_format: type[TextFormatT] | Omit = omit, + starting_after: int | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, @@ -890,31 +890,31 @@ def stream( *, input: Union[str, ResponseInputParam], model: ResponsesModel, - background: Optional[bool] | NotGiven = NOT_GIVEN, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -926,35 +926,35 @@ def stream( def stream( self, *, - response_id: str | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - background: Optional[bool] | NotGiven = NOT_GIVEN, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + response_id: str | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + model: ResponsesModel | Omit = omit, + background: Optional[bool] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1061,7 +1061,7 @@ def stream( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, - starting_after=NOT_GIVEN, + starting_after=omit, timeout=timeout, ), text_format=text_format, @@ -1072,35 +1072,35 @@ def stream( def parse( self, *, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + text_format: type[TextFormatT] | Omit = omit, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1178,16 +1178,16 @@ def retrieve( self, response_id: str, *, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, - stream: Literal[False] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response: ... @overload @@ -1196,8 +1196,8 @@ def retrieve( response_id: str, *, stream: Literal[True], - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1212,8 +1212,8 @@ def retrieve( response_id: str, *, stream: bool, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1228,8 +1228,8 @@ def retrieve( response_id: str, *, stream: bool = False, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1276,15 +1276,15 @@ def retrieve( response_id: str, *, stream: Literal[True], - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[ResponseStreamEvent]: """ Retrieves a model response with the given ID. @@ -1325,15 +1325,15 @@ def retrieve( response_id: str, *, stream: bool, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | Stream[ResponseStreamEvent]: """ Retrieves a model response with the given ID. @@ -1372,16 +1372,16 @@ def retrieve( self, response_id: str, *, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, - stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | Stream[ResponseStreamEvent]: if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") @@ -1416,7 +1416,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Deletes a model response with the given ID. @@ -1450,7 +1450,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response: """Cancels a model response with the given ID. @@ -1506,39 +1506,39 @@ def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse: async def create( self, *, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response: """Creates a model response. @@ -1746,38 +1746,38 @@ async def create( self, *, stream: Literal[True], - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[ResponseStreamEvent]: """Creates a model response. @@ -1985,38 +1985,38 @@ async def create( self, *, stream: bool, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | AsyncStream[ResponseStreamEvent]: """Creates a model response. @@ -2222,39 +2222,39 @@ async def create( async def create( self, *, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | AsyncStream[ResponseStreamEvent]: return await self._post( "/responses", @@ -2305,9 +2305,9 @@ def stream( self, *, response_id: str, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, + text_format: type[TextFormatT] | Omit = omit, + starting_after: int | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, @@ -2321,31 +2321,31 @@ def stream( *, input: Union[str, ResponseInputParam], model: ResponsesModel, - background: Optional[bool] | NotGiven = NOT_GIVEN, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, + background: Optional[bool] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2357,35 +2357,35 @@ def stream( def stream( self, *, - response_id: str | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - background: Optional[bool] | NotGiven = NOT_GIVEN, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + response_id: str | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + model: ResponsesModel | Omit = omit, + background: Optional[bool] | Omit = omit, + text_format: type[TextFormatT] | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2486,7 +2486,7 @@ def stream( starting_after=None, ) else: - if isinstance(response_id, NotGiven): + if isinstance(response_id, Omit): raise ValueError("response_id must be provided when streaming an existing response") api_request = self.retrieve( @@ -2508,35 +2508,35 @@ def stream( async def parse( self, *, - text_format: type[TextFormatT] | NotGiven = NOT_GIVEN, - background: Optional[bool] | NotGiven = NOT_GIVEN, - conversation: Optional[response_create_params.Conversation] | NotGiven = NOT_GIVEN, - include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN, - input: Union[str, ResponseInputParam] | NotGiven = NOT_GIVEN, - instructions: Optional[str] | NotGiven = NOT_GIVEN, - max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN, - max_tool_calls: Optional[int] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - model: ResponsesModel | NotGiven = NOT_GIVEN, - parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN, - previous_response_id: Optional[str] | NotGiven = NOT_GIVEN, - prompt: Optional[ResponsePromptParam] | NotGiven = NOT_GIVEN, - prompt_cache_key: str | NotGiven = NOT_GIVEN, - reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN, - safety_identifier: str | NotGiven = NOT_GIVEN, - service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | NotGiven = NOT_GIVEN, - store: Optional[bool] | NotGiven = NOT_GIVEN, - stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN, - stream_options: Optional[response_create_params.StreamOptions] | NotGiven = NOT_GIVEN, - temperature: Optional[float] | NotGiven = NOT_GIVEN, - text: ResponseTextConfigParam | NotGiven = NOT_GIVEN, - tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN, - tools: Iterable[ParseableToolParam] | NotGiven = NOT_GIVEN, - top_logprobs: Optional[int] | NotGiven = NOT_GIVEN, - top_p: Optional[float] | NotGiven = NOT_GIVEN, - truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN, - user: str | NotGiven = NOT_GIVEN, - verbosity: Optional[Literal["low", "medium", "high"]] | NotGiven = NOT_GIVEN, + text_format: type[TextFormatT] | Omit = omit, + background: Optional[bool] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[ParseableToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + verbosity: Optional[Literal["low", "medium", "high"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2614,16 +2614,16 @@ async def retrieve( self, response_id: str, *, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, - stream: Literal[False] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response: ... @overload @@ -2632,8 +2632,8 @@ async def retrieve( response_id: str, *, stream: Literal[True], - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2648,8 +2648,8 @@ async def retrieve( response_id: str, *, stream: bool, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2664,8 +2664,8 @@ async def retrieve( response_id: str, *, stream: bool = False, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2712,15 +2712,15 @@ async def retrieve( response_id: str, *, stream: Literal[True], - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[ResponseStreamEvent]: """ Retrieves a model response with the given ID. @@ -2761,15 +2761,15 @@ async def retrieve( response_id: str, *, stream: bool, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | AsyncStream[ResponseStreamEvent]: """ Retrieves a model response with the given ID. @@ -2808,16 +2808,16 @@ async def retrieve( self, response_id: str, *, - include: List[ResponseIncludable] | NotGiven = NOT_GIVEN, - include_obfuscation: bool | NotGiven = NOT_GIVEN, - starting_after: int | NotGiven = NOT_GIVEN, - stream: Literal[False] | Literal[True] | NotGiven = NOT_GIVEN, + include: List[ResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response | AsyncStream[ResponseStreamEvent]: if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") @@ -2852,7 +2852,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ Deletes a model response with the given ID. @@ -2886,7 +2886,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Response: """Cancels a model response with the given ID. @@ -3008,9 +3008,9 @@ def input_items(self) -> AsyncInputItemsWithStreamingResponse: return AsyncInputItemsWithStreamingResponse(self._responses.input_items) -def _make_tools(tools: Iterable[ParseableToolParam] | NotGiven) -> List[ToolParam] | NotGiven: +def _make_tools(tools: Iterable[ParseableToolParam] | Omit) -> List[ToolParam] | Omit: if not is_given(tools): - return NOT_GIVEN + return omit converted_tools: List[ToolParam] = [] for tool in tools: diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index a32f4eb1d2..73eabd4083 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -7,7 +7,7 @@ import httpx from ... import _legacy_response -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import Body, Query, Headers, NotGiven, FileTypes, not_given from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -49,7 +49,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> UploadPart: """ Adds a @@ -124,7 +124,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> UploadPart: """ Adds a diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 8811bed48c..8953256f2a 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -22,7 +22,7 @@ AsyncPartsWithStreamingResponse, ) from ...types import FilePurpose, upload_create_params, upload_complete_params -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -73,7 +73,7 @@ def upload_file_chunked( purpose: FilePurpose, bytes: int | None = None, part_size: int | None = None, - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, ) -> Upload: """Splits a file into multiple 64MB parts and uploads them sequentially.""" @@ -87,7 +87,7 @@ def upload_file_chunked( mime_type: str, purpose: FilePurpose, part_size: int | None = None, - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, ) -> Upload: """Splits an in-memory file into multiple 64MB parts and uploads them sequentially.""" @@ -100,7 +100,7 @@ def upload_file_chunked( filename: str | None = None, bytes: int | None = None, part_size: int | None = None, - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, ) -> Upload: """Splits the given file into multiple parts and uploads them sequentially. @@ -170,13 +170,13 @@ def create( filename: str, mime_type: str, purpose: FilePurpose, - expires_after: upload_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + expires_after: upload_create_params.ExpiresAfter | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Upload: """ Creates an intermediate @@ -252,7 +252,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Upload: """Cancels the Upload. @@ -282,13 +282,13 @@ def complete( upload_id: str, *, part_ids: SequenceNotStr[str], - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Upload: """ Completes the @@ -370,7 +370,7 @@ async def upload_file_chunked( purpose: FilePurpose, bytes: int | None = None, part_size: int | None = None, - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, ) -> Upload: """Splits a file into multiple 64MB parts and uploads them sequentially.""" @@ -384,7 +384,7 @@ async def upload_file_chunked( mime_type: str, purpose: FilePurpose, part_size: int | None = None, - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, ) -> Upload: """Splits an in-memory file into multiple 64MB parts and uploads them sequentially.""" @@ -397,7 +397,7 @@ async def upload_file_chunked( filename: str | None = None, bytes: int | None = None, part_size: int | None = None, - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, ) -> Upload: """Splits the given file into multiple parts and uploads them sequentially. @@ -478,13 +478,13 @@ async def create( filename: str, mime_type: str, purpose: FilePurpose, - expires_after: upload_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, + expires_after: upload_create_params.ExpiresAfter | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Upload: """ Creates an intermediate @@ -560,7 +560,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Upload: """Cancels the Upload. @@ -590,13 +590,13 @@ async def complete( upload_id: str, *, part_ids: SequenceNotStr[str], - md5: str | NotGiven = NOT_GIVEN, + md5: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Upload: """ Completes the diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index adf399d8de..0f989821de 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -12,7 +12,7 @@ from ... import _legacy_response from ...types import FileChunkingStrategyParam -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes, SequenceNotStr +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given from ..._utils import is_given, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -53,14 +53,14 @@ def create( vector_store_id: str, *, file_ids: SequenceNotStr[str], - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """ Create a vector store file batch. @@ -116,7 +116,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """ Retrieves a vector store file batch. @@ -153,7 +153,7 @@ def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """Cancel a vector store file batch. @@ -187,8 +187,8 @@ def create_and_poll( vector_store_id: str, *, file_ids: SequenceNotStr[str], - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = self.create( @@ -208,17 +208,17 @@ def list_files( batch_id: str, *, vector_store_id: str, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[VectorStoreFile]: """ Returns a list of vector store files in a batch. @@ -282,7 +282,7 @@ def poll( batch_id: str, *, vector_store_id: str, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, ) -> VectorStoreFileBatch: """Wait for the given file batch to be processed. @@ -321,8 +321,8 @@ def upload_and_poll( files: Iterable[FileTypes], max_concurrency: int = 5, file_ids: SequenceNotStr[str] = [], - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFileBatch: """Uploads the given files concurrently and then creates a vector store file batch. @@ -390,14 +390,14 @@ async def create( vector_store_id: str, *, file_ids: SequenceNotStr[str], - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """ Create a vector store file batch. @@ -453,7 +453,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """ Retrieves a vector store file batch. @@ -490,7 +490,7 @@ async def cancel( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """Cancel a vector store file batch. @@ -524,8 +524,8 @@ async def create_and_poll( vector_store_id: str, *, file_ids: SequenceNotStr[str], - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = await self.create( @@ -545,17 +545,17 @@ def list_files( batch_id: str, *, vector_store_id: str, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]: """ Returns a list of vector store files in a batch. @@ -619,7 +619,7 @@ async def poll( batch_id: str, *, vector_store_id: str, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, ) -> VectorStoreFileBatch: """Wait for the given file batch to be processed. @@ -658,8 +658,8 @@ async def upload_and_poll( files: Iterable[FileTypes], max_concurrency: int = 5, file_ids: SequenceNotStr[str] = [], - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFileBatch: """Uploads the given files concurrently and then creates a vector store file batch. diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py index 2c90bb7a1f..d2eb4e16ed 100644 --- a/src/openai/resources/vector_stores/files.py +++ b/src/openai/resources/vector_stores/files.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ...types import FileChunkingStrategyParam -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes +from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given from ..._utils import is_given, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -50,14 +50,14 @@ def create( vector_store_id: str, *, file_id: str, - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """ Create a vector store file by attaching a @@ -115,7 +115,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """ Retrieves a vector store file. @@ -153,7 +153,7 @@ def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """ Update attributes on a vector store file. @@ -191,17 +191,17 @@ def list( self, vector_store_id: str, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[VectorStoreFile]: """ Returns a list of vector store files. @@ -268,7 +268,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileDeleted: """Delete a vector store file. @@ -304,9 +304,9 @@ def create_and_poll( file_id: str, *, vector_store_id: str, - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" self.create( @@ -324,7 +324,7 @@ def poll( file_id: str, *, vector_store_id: str, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, ) -> VectorStoreFile: """Wait for the vector store file to finish processing. @@ -365,7 +365,7 @@ def upload( *, vector_store_id: str, file: FileTypes, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFile: """Upload a file to the `files` API and then attach it to the given vector store. @@ -380,9 +380,9 @@ def upload_and_poll( *, vector_store_id: str, file: FileTypes, - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFile: """Add a file to a vector store and poll until processing is complete.""" file_obj = self._client.files.create(file=file, purpose="assistants") @@ -404,7 +404,7 @@ def content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[FileContentResponse]: """ Retrieve the parsed contents of a vector store file. @@ -458,14 +458,14 @@ async def create( vector_store_id: str, *, file_id: str, - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """ Create a vector store file by attaching a @@ -523,7 +523,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """ Retrieves a vector store file. @@ -561,7 +561,7 @@ async def update( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """ Update attributes on a vector store file. @@ -599,17 +599,17 @@ def list( self, vector_store_id: str, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - filter: Literal["in_progress", "completed", "failed", "cancelled"] | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[VectorStoreFile, AsyncCursorPage[VectorStoreFile]]: """ Returns a list of vector store files. @@ -676,7 +676,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileDeleted: """Delete a vector store file. @@ -712,9 +712,9 @@ async def create_and_poll( file_id: str, *, vector_store_id: str, - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" await self.create( @@ -732,7 +732,7 @@ async def poll( file_id: str, *, vector_store_id: str, - poll_interval_ms: int | NotGiven = NOT_GIVEN, + poll_interval_ms: int | Omit = omit, ) -> VectorStoreFile: """Wait for the vector store file to finish processing. @@ -773,7 +773,7 @@ async def upload( *, vector_store_id: str, file: FileTypes, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFile: """Upload a file to the `files` API and then attach it to the given vector store. @@ -790,9 +790,9 @@ async def upload_and_poll( *, vector_store_id: str, file: FileTypes, - attributes: Optional[Dict[str, Union[str, float, bool]]] | NotGiven = NOT_GIVEN, - poll_interval_ms: int | NotGiven = NOT_GIVEN, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, + poll_interval_ms: int | Omit = omit, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, ) -> VectorStoreFile: """Add a file to a vector store and poll until processing is complete.""" file_obj = await self._client.files.create(file=file, purpose="assistants") @@ -814,7 +814,7 @@ def content( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[FileContentResponse, AsyncPage[FileContentResponse]]: """ Retrieve the parsed contents of a vector store file. diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py index 4f211ea25a..39548936c8 100644 --- a/src/openai/resources/vector_stores/vector_stores.py +++ b/src/openai/resources/vector_stores/vector_stores.py @@ -23,7 +23,7 @@ vector_store_search_params, vector_store_update_params, ) -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, SequenceNotStr +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -78,17 +78,17 @@ def with_streaming_response(self) -> VectorStoresWithStreamingResponse: def create( self, *, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, - expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + expires_after: vector_store_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStore: """ Create a vector store. @@ -148,7 +148,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStore: """ Retrieves a vector store. @@ -177,15 +177,15 @@ def update( self, vector_store_id: str, *, - expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: Optional[str] | NotGiven = NOT_GIVEN, + expires_after: Optional[vector_store_update_params.ExpiresAfter] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStore: """ Modifies a vector store. @@ -232,16 +232,16 @@ def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[VectorStore]: """Returns a list of vector stores. @@ -303,7 +303,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreDeleted: """ Delete a vector store. @@ -333,16 +333,16 @@ def search( vector_store_id: str, *, query: Union[str, SequenceNotStr[str]], - filters: vector_store_search_params.Filters | NotGiven = NOT_GIVEN, - max_num_results: int | NotGiven = NOT_GIVEN, - ranking_options: vector_store_search_params.RankingOptions | NotGiven = NOT_GIVEN, - rewrite_query: bool | NotGiven = NOT_GIVEN, + filters: vector_store_search_params.Filters | Omit = omit, + max_num_results: int | Omit = omit, + ranking_options: vector_store_search_params.RankingOptions | Omit = omit, + rewrite_query: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[VectorStoreSearchResponse]: """ Search a vector store for relevant chunks based on a query and file attributes @@ -423,17 +423,17 @@ def with_streaming_response(self) -> AsyncVectorStoresWithStreamingResponse: async def create( self, *, - chunking_strategy: FileChunkingStrategyParam | NotGiven = NOT_GIVEN, - expires_after: vector_store_create_params.ExpiresAfter | NotGiven = NOT_GIVEN, - file_ids: SequenceNotStr[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: str | NotGiven = NOT_GIVEN, + chunking_strategy: FileChunkingStrategyParam | Omit = omit, + expires_after: vector_store_create_params.ExpiresAfter | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStore: """ Create a vector store. @@ -493,7 +493,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStore: """ Retrieves a vector store. @@ -522,15 +522,15 @@ async def update( self, vector_store_id: str, *, - expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN, - metadata: Optional[Metadata] | NotGiven = NOT_GIVEN, - name: Optional[str] | NotGiven = NOT_GIVEN, + expires_after: Optional[vector_store_update_params.ExpiresAfter] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStore: """ Modifies a vector store. @@ -577,16 +577,16 @@ async def update( def list( self, *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - limit: int | NotGiven = NOT_GIVEN, - order: Literal["asc", "desc"] | NotGiven = NOT_GIVEN, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[VectorStore, AsyncCursorPage[VectorStore]]: """Returns a list of vector stores. @@ -648,7 +648,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreDeleted: """ Delete a vector store. @@ -678,16 +678,16 @@ def search( vector_store_id: str, *, query: Union[str, SequenceNotStr[str]], - filters: vector_store_search_params.Filters | NotGiven = NOT_GIVEN, - max_num_results: int | NotGiven = NOT_GIVEN, - ranking_options: vector_store_search_params.RankingOptions | NotGiven = NOT_GIVEN, - rewrite_query: bool | NotGiven = NOT_GIVEN, + filters: vector_store_search_params.Filters | Omit = omit, + max_num_results: int | Omit = omit, + ranking_options: vector_store_search_params.RankingOptions | Omit = omit, + rewrite_query: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[VectorStoreSearchResponse, AsyncPage[VectorStoreSearchResponse]]: """ Search a vector store for relevant chunks based on a query and file attributes diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 482d4e75c1..8dd2bd5981 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -34,6 +34,7 @@ WebSearchToolFilters = web_search_tool.Filters WebSearchToolUserLocation = web_search_tool.UserLocation + class McpAllowedToolsMcpToolFilter(BaseModel): read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 54bc271c0f..e84abc4390 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -36,6 +36,7 @@ WebSearchToolFilters = web_search_tool_param.Filters WebSearchToolUserLocation = web_search_tool_param.UserLocation + class McpAllowedToolsMcpToolFilter(TypedDict, total=False): read_only: bool """Indicates whether or not a tool modifies data or is read-only. diff --git a/tests/test_transform.py b/tests/test_transform.py index 036cfdfb06..bece75dfc7 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -8,7 +8,7 @@ import pytest -from openai._types import NOT_GIVEN, Base64FileInput +from openai._types import Base64FileInput, omit, not_given from openai._utils import ( PropertyInfo, transform as _transform, @@ -450,4 +450,11 @@ async def test_transform_skipping(use_async: bool) -> None: @pytest.mark.asyncio async def test_strips_notgiven(use_async: bool) -> None: assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} - assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {} From d296d85884cac05917a2776cc22c632027e8eb05 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 05:03:36 +0000 Subject: [PATCH 110/408] feat(api): add reasoning_text --- .stats.yml | 6 +++--- src/openai/types/conversations/message.py | 12 +++++++++++- .../types/conversations/summary_text_content.py | 2 ++ .../response_content_part_added_event.py | 15 +++++++++++++-- .../responses/response_content_part_done_event.py | 15 +++++++++++++-- .../types/responses/response_reasoning_item.py | 4 ++-- .../responses/response_reasoning_item_param.py | 4 ++-- 7 files changed, 46 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2dd0aef46a..c961e232cf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-380330a93b5d010391ca3b36ea193c5353b0dfdf2ddd02789ef84a84ce427e82.yml -openapi_spec_hash: 859703234259ecdd2a3c6f4de88eb504 -config_hash: b619b45c1e7facf819f902dee8fa4f97 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-ea23db36b0899cc715f56d0098956069b2d92880f448adff3a4ac1bb53cb2cec.yml +openapi_spec_hash: 36f76ea31297c9593bcfae453f6255cc +config_hash: 666d6bb4b564f0d9d431124b5d1a0665 diff --git a/src/openai/types/conversations/message.py b/src/openai/types/conversations/message.py index 95e03c5c00..dbf5a14680 100644 --- a/src/openai/types/conversations/message.py +++ b/src/openai/types/conversations/message.py @@ -14,7 +14,16 @@ from ..responses.response_output_text import ResponseOutputText from ..responses.response_output_refusal import ResponseOutputRefusal -__all__ = ["Message", "Content"] +__all__ = ["Message", "Content", "ContentReasoningText"] + + +class ContentReasoningText(BaseModel): + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + Content: TypeAlias = Annotated[ Union[ @@ -22,6 +31,7 @@ ResponseOutputText, TextContent, SummaryTextContent, + ContentReasoningText, ResponseOutputRefusal, ResponseInputImage, ComputerScreenshotContent, diff --git a/src/openai/types/conversations/summary_text_content.py b/src/openai/types/conversations/summary_text_content.py index 047769ed67..d357b15725 100644 --- a/src/openai/types/conversations/summary_text_content.py +++ b/src/openai/types/conversations/summary_text_content.py @@ -9,5 +9,7 @@ class SummaryTextContent(BaseModel): text: str + """A summary of the reasoning output from the model so far.""" type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" diff --git a/src/openai/types/responses/response_content_part_added_event.py b/src/openai/types/responses/response_content_part_added_event.py index 11e0ac7c92..c78e80d1c4 100644 --- a/src/openai/types/responses/response_content_part_added_event.py +++ b/src/openai/types/responses/response_content_part_added_event.py @@ -8,9 +8,20 @@ from .response_output_text import ResponseOutputText from .response_output_refusal import ResponseOutputRefusal -__all__ = ["ResponseContentPartAddedEvent", "Part"] +__all__ = ["ResponseContentPartAddedEvent", "Part", "PartReasoningText"] -Part: TypeAlias = Annotated[Union[ResponseOutputText, ResponseOutputRefusal], PropertyInfo(discriminator="type")] + +class PartReasoningText(BaseModel): + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[ResponseOutputText, ResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] class ResponseContentPartAddedEvent(BaseModel): diff --git a/src/openai/types/responses/response_content_part_done_event.py b/src/openai/types/responses/response_content_part_done_event.py index e1b411bb45..732f2303ef 100644 --- a/src/openai/types/responses/response_content_part_done_event.py +++ b/src/openai/types/responses/response_content_part_done_event.py @@ -8,9 +8,20 @@ from .response_output_text import ResponseOutputText from .response_output_refusal import ResponseOutputRefusal -__all__ = ["ResponseContentPartDoneEvent", "Part"] +__all__ = ["ResponseContentPartDoneEvent", "Part", "PartReasoningText"] -Part: TypeAlias = Annotated[Union[ResponseOutputText, ResponseOutputRefusal], PropertyInfo(discriminator="type")] + +class PartReasoningText(BaseModel): + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[ResponseOutputText, ResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] class ResponseContentPartDoneEvent(BaseModel): diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py index e5cb094e62..fc582cf7c5 100644 --- a/src/openai/types/responses/response_reasoning_item.py +++ b/src/openai/types/responses/response_reasoning_item.py @@ -18,10 +18,10 @@ class Summary(BaseModel): class Content(BaseModel): text: str - """Reasoning text output from the model.""" + """The reasoning text from the model.""" type: Literal["reasoning_text"] - """The type of the object. Always `reasoning_text`.""" + """The type of the reasoning text. Always `reasoning_text`.""" class ResponseReasoningItem(BaseModel): diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py index 042b6c05db..56e88ba28d 100644 --- a/src/openai/types/responses/response_reasoning_item_param.py +++ b/src/openai/types/responses/response_reasoning_item_param.py @@ -18,10 +18,10 @@ class Summary(TypedDict, total=False): class Content(TypedDict, total=False): text: Required[str] - """Reasoning text output from the model.""" + """The reasoning text from the model.""" type: Required[Literal["reasoning_text"]] - """The type of the object. Always `reasoning_text`.""" + """The type of the reasoning text. Always `reasoning_text`.""" class ResponseReasoningItemParam(TypedDict, total=False): From 71dedfad6716c241744d3bd856370e8c59e75500 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 05:04:29 +0000 Subject: [PATCH 111/408] release: 1.108.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 102fa47016..118bf88182 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.108.0" + ".": "1.108.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e35189611..897ba0d1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.108.1 (2025-09-19) + +Full Changelog: [v1.108.0...v1.108.1](https://github.com/openai/openai-python/compare/v1.108.0...v1.108.1) + +### Features + +* **api:** add reasoning_text ([18d8e12](https://github.com/openai/openai-python/commit/18d8e12061d1fd4e09d24986ff6e38c5063013e9)) + + +### Chores + +* **types:** change optional parameter type from NotGiven to Omit ([acc190a](https://github.com/openai/openai-python/commit/acc190a29526e64db6074e7f21aca800423c128c)) + ## 1.108.0 (2025-09-17) Full Changelog: [v1.107.3...v1.108.0](https://github.com/openai/openai-python/compare/v1.107.3...v1.108.0) diff --git a/pyproject.toml b/pyproject.toml index 058b7cda6c..5adf4a2a8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.108.0" +version = "1.108.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7030fe068c..8ba4e8e168 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.108.0" # x-release-please-version +__version__ = "1.108.1" # x-release-please-version From 9272e61afa41b1e8223fdccc4935f55e7b72d11b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 17:41:05 +0000 Subject: [PATCH 112/408] chore: do not install brew dependencies in ./scripts/bootstrap by default --- scripts/bootstrap | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index 9910ec05fc..953993addb 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependencies…" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi From bfed4af9be93e911111299c34da1baf324cbea99 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 21 Sep 2025 05:33:13 +0000 Subject: [PATCH 113/408] fix(api): fix mcp tool name --- .stats.yml | 4 ++-- src/openai/types/realtime/realtime_mcp_tool_call.py | 4 ++-- src/openai/types/realtime/realtime_mcp_tool_call_param.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index c961e232cf..66c059ae58 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-ea23db36b0899cc715f56d0098956069b2d92880f448adff3a4ac1bb53cb2cec.yml -openapi_spec_hash: 36f76ea31297c9593bcfae453f6255cc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-65d42621b731238ad4e59a35a705fc0608b17f53a14d047e66ce480c793da26b.yml +openapi_spec_hash: d7ca86b2507600cbd5ed197cf31263c2 config_hash: 666d6bb4b564f0d9d431124b5d1a0665 diff --git a/src/openai/types/realtime/realtime_mcp_tool_call.py b/src/openai/types/realtime/realtime_mcp_tool_call.py index 533175e55b..019aee25c0 100644 --- a/src/openai/types/realtime/realtime_mcp_tool_call.py +++ b/src/openai/types/realtime/realtime_mcp_tool_call.py @@ -30,8 +30,8 @@ class RealtimeMcpToolCall(BaseModel): server_label: str """The label of the MCP server running the tool.""" - type: Literal["mcp_tool_call"] - """The type of the item. Always `mcp_tool_call`.""" + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" approval_request_id: Optional[str] = None """The ID of an associated approval request, if any.""" diff --git a/src/openai/types/realtime/realtime_mcp_tool_call_param.py b/src/openai/types/realtime/realtime_mcp_tool_call_param.py index afdc9d1d17..0ba16d3dc1 100644 --- a/src/openai/types/realtime/realtime_mcp_tool_call_param.py +++ b/src/openai/types/realtime/realtime_mcp_tool_call_param.py @@ -27,8 +27,8 @@ class RealtimeMcpToolCallParam(TypedDict, total=False): server_label: Required[str] """The label of the MCP server running the tool.""" - type: Required[Literal["mcp_tool_call"]] - """The type of the item. Always `mcp_tool_call`.""" + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" approval_request_id: Optional[str] """The ID of an associated approval request, if any.""" From 3a3cabb7e140f0a462e4e3aa4f9f2902bb7a2a92 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:25:50 +0000 Subject: [PATCH 114/408] chore: improve example values --- tests/api_resources/conversations/test_items.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/api_resources/conversations/test_items.py b/tests/api_resources/conversations/test_items.py index c308160543..0df88dc199 100644 --- a/tests/api_resources/conversations/test_items.py +++ b/tests/api_resources/conversations/test_items.py @@ -30,6 +30,7 @@ def test_method_create(self, client: OpenAI) -> None: { "content": "string", "role": "user", + "type": "message", } ], ) @@ -58,6 +59,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: { "content": "string", "role": "user", + "type": "message", } ], ) @@ -75,6 +77,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None: { "content": "string", "role": "user", + "type": "message", } ], ) as response: @@ -95,6 +98,7 @@ def test_path_params_create(self, client: OpenAI) -> None: { "content": "string", "role": "user", + "type": "message", } ], ) @@ -267,6 +271,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: { "content": "string", "role": "user", + "type": "message", } ], ) @@ -295,6 +300,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: { "content": "string", "role": "user", + "type": "message", } ], ) @@ -312,6 +318,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non { "content": "string", "role": "user", + "type": "message", } ], ) as response: @@ -332,6 +339,7 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: { "content": "string", "role": "user", + "type": "message", } ], ) From 58add648f119140bf108931371e0811601e977c3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 23:20:40 +0000 Subject: [PATCH 115/408] chore(api): openapi updates for conversations --- .stats.yml | 4 +- .../resources/conversations/conversations.py | 38 ++++++++++--------- .../conversation_create_params.py | 6 +-- .../conversation_update_params.py | 13 ++++--- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/.stats.yml b/.stats.yml index 66c059ae58..062111e2c4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-65d42621b731238ad4e59a35a705fc0608b17f53a14d047e66ce480c793da26b.yml -openapi_spec_hash: d7ca86b2507600cbd5ed197cf31263c2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-937fcfac8cbab692796cd9822b37e48a311e2220a8b103106ded0ee92a0b9484.yml +openapi_spec_hash: 74a0c58b5b8c4e06792d79b685e02a01 config_hash: 666d6bb4b564f0d9d431124b5d1a0665 diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py index 4b942eb014..da037a4e22 100644 --- a/src/openai/resources/conversations/conversations.py +++ b/src/openai/resources/conversations/conversations.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Iterable, Optional +from typing import Iterable, Optional import httpx @@ -115,7 +115,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ - Get a conversation with the given ID. + Get a conversation Args: extra_headers: Send extra headers @@ -140,7 +140,7 @@ def update( self, conversation_id: str, *, - metadata: Dict[str, str], + metadata: Optional[Metadata], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -149,14 +149,15 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ - Update a conversation's metadata with the given ID. + Update a conversation Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. Keys are strings with a maximum - length of 64 characters. Values are strings with a maximum length of 512 - characters. + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -188,8 +189,9 @@ def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationDeletedResource: - """ - Delete a conversation with the given ID. + """Delete a conversation. + + Items in the conversation will not be deleted. Args: extra_headers: Send extra headers @@ -296,7 +298,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ - Get a conversation with the given ID. + Get a conversation Args: extra_headers: Send extra headers @@ -321,7 +323,7 @@ async def update( self, conversation_id: str, *, - metadata: Dict[str, str], + metadata: Optional[Metadata], # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -330,14 +332,15 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Conversation: """ - Update a conversation's metadata with the given ID. + Update a conversation Args: metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. Keys are strings with a maximum - length of 64 characters. Values are strings with a maximum length of 512 - characters. + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. extra_headers: Send extra headers @@ -371,8 +374,9 @@ async def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ConversationDeletedResource: - """ - Delete a conversation with the given ID. + """Delete a conversation. + + Items in the conversation will not be deleted. Args: extra_headers: Send extra headers diff --git a/src/openai/types/conversations/conversation_create_params.py b/src/openai/types/conversations/conversation_create_params.py index 0d84f503bd..5f38d2aca7 100644 --- a/src/openai/types/conversations/conversation_create_params.py +++ b/src/openai/types/conversations/conversation_create_params.py @@ -13,9 +13,9 @@ class ConversationCreateParams(TypedDict, total=False): items: Optional[Iterable[ResponseInputItemParam]] - """ - Initial items to include in the conversation context. You may add up to 20 items - at a time. + """Initial items to include in the conversation context. + + You may add up to 20 items at a time. """ metadata: Optional[Metadata] diff --git a/src/openai/types/conversations/conversation_update_params.py b/src/openai/types/conversations/conversation_update_params.py index f2aa42d833..1f0dd09e50 100644 --- a/src/openai/types/conversations/conversation_update_params.py +++ b/src/openai/types/conversations/conversation_update_params.py @@ -2,18 +2,21 @@ from __future__ import annotations -from typing import Dict +from typing import Optional from typing_extensions import Required, TypedDict +from ..shared_params.metadata import Metadata + __all__ = ["ConversationUpdateParams"] class ConversationUpdateParams(TypedDict, total=False): - metadata: Required[Dict[str, str]] + metadata: Required[Optional[Metadata]] """Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a - structured format, and querying for objects via API or the dashboard. Keys are - strings with a maximum length of 64 characters. Values are strings with a - maximum length of 512 characters. + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. """ From 02af9aacd14805cbca21078d32a311758360f134 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 23:21:08 +0000 Subject: [PATCH 116/408] release: 1.108.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 118bf88182..e66e9ab9f4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.108.1" + ".": "1.108.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 897ba0d1bc..34d2e24899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.108.2 (2025-09-22) + +Full Changelog: [v1.108.1...v1.108.2](https://github.com/openai/openai-python/compare/v1.108.1...v1.108.2) + +### Bug Fixes + +* **api:** fix mcp tool name ([fd1c673](https://github.com/openai/openai-python/commit/fd1c673fa8d5581b38c69c37aa4fd1fd251259a2)) + + +### Chores + +* **api:** openapi updates for conversations ([3224f6f](https://github.com/openai/openai-python/commit/3224f6f9b4221b954a8f63de66bcaab389164ee5)) +* do not install brew dependencies in ./scripts/bootstrap by default ([6764b00](https://github.com/openai/openai-python/commit/6764b00bcb8aeab41e73d2fcaf6c7a18ea9f7909)) +* improve example values ([20b58e1](https://github.com/openai/openai-python/commit/20b58e164f9f28b9fc562968263fa3eacc6f5c7c)) + ## 1.108.1 (2025-09-19) Full Changelog: [v1.108.0...v1.108.1](https://github.com/openai/openai-python/compare/v1.108.0...v1.108.1) diff --git a/pyproject.toml b/pyproject.toml index 5adf4a2a8c..01d7c4e4a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.108.1" +version = "1.108.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 8ba4e8e168..a266f4ecdb 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.108.1" # x-release-please-version +__version__ = "1.108.2" # x-release-please-version From c523e639bb0b041562aa2a1b511ddf032e4a719a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:55:28 +0000 Subject: [PATCH 117/408] feat(api): gpt-5-codex --- .stats.yml | 4 ++-- src/openai/types/shared/all_models.py | 1 + src/openai/types/shared/responses_model.py | 1 + src/openai/types/shared_params/responses_model.py | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 062111e2c4..48863a6e93 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-937fcfac8cbab692796cd9822b37e48a311e2220a8b103106ded0ee92a0b9484.yml -openapi_spec_hash: 74a0c58b5b8c4e06792d79b685e02a01 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-410219ea680089f02bb55163c673919703f946c3d6ad7ff5d6f607121d5287d5.yml +openapi_spec_hash: 2b3eee95d3f6796c7a61dfddf694a59a config_hash: 666d6bb4b564f0d9d431124b5d1a0665 diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py index 828f3b5669..76ca1ffd29 100644 --- a/src/openai/types/shared/all_models.py +++ b/src/openai/types/shared/all_models.py @@ -21,5 +21,6 @@ "o4-mini-deep-research-2025-06-26", "computer-use-preview", "computer-use-preview-2025-03-11", + "gpt-5-codex", ], ] diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py index 4d35356806..4fbdce8db9 100644 --- a/src/openai/types/shared/responses_model.py +++ b/src/openai/types/shared/responses_model.py @@ -21,5 +21,6 @@ "o4-mini-deep-research-2025-06-26", "computer-use-preview", "computer-use-preview-2025-03-11", + "gpt-5-codex", ], ] diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py index adfcecf1e5..2feaa22b67 100644 --- a/src/openai/types/shared_params/responses_model.py +++ b/src/openai/types/shared_params/responses_model.py @@ -23,5 +23,6 @@ "o4-mini-deep-research-2025-06-26", "computer-use-preview", "computer-use-preview-2025-03-11", + "gpt-5-codex", ], ] From 9c4b995682f664c629d681c975496a99c793c06d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:55:58 +0000 Subject: [PATCH 118/408] release: 1.109.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e66e9ab9f4..529006a8d5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.108.2" + ".": "1.109.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d2e24899..15e8f62701 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.109.0 (2025-09-23) + +Full Changelog: [v1.108.2...v1.109.0](https://github.com/openai/openai-python/compare/v1.108.2...v1.109.0) + +### Features + +* **api:** gpt-5-codex ([34502b5](https://github.com/openai/openai-python/commit/34502b5a175f8a10ea8694fcea38fe7308de89ef)) + ## 1.108.2 (2025-09-22) Full Changelog: [v1.108.1...v1.108.2](https://github.com/openai/openai-python/compare/v1.108.1...v1.108.2) diff --git a/pyproject.toml b/pyproject.toml index 01d7c4e4a2..26dfbcf443 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.108.2" +version = "1.109.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a266f4ecdb..16f48fb404 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.108.2" # x-release-please-version +__version__ = "1.109.0" # x-release-please-version From edb8e106bf41937e1da9644250945665bc7a4caa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:05:32 +0000 Subject: [PATCH 119/408] fix(compat): compat with `pydantic<2.8.0` when using additional fields --- src/openai/types/evals/runs/output_item_list_response.py | 7 ++++++- .../types/evals/runs/output_item_retrieve_response.py | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/openai/types/evals/runs/output_item_list_response.py b/src/openai/types/evals/runs/output_item_list_response.py index f774518f3c..e88c21766f 100644 --- a/src/openai/types/evals/runs/output_item_list_response.py +++ b/src/openai/types/evals/runs/output_item_list_response.py @@ -27,12 +27,17 @@ class Result(BaseModel): type: Optional[str] = None """The grader type (for example, "string-check-grader").""" - __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + # Stub to indicate that arbitrary properties are accepted. # To access properties that are not valid identifiers you can use `getattr`, e.g. # `getattr(obj, '$type')` def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] class SampleInput(BaseModel): diff --git a/src/openai/types/evals/runs/output_item_retrieve_response.py b/src/openai/types/evals/runs/output_item_retrieve_response.py index d66435bd4f..c728629b41 100644 --- a/src/openai/types/evals/runs/output_item_retrieve_response.py +++ b/src/openai/types/evals/runs/output_item_retrieve_response.py @@ -27,12 +27,17 @@ class Result(BaseModel): type: Optional[str] = None """The grader type (for example, "string-check-grader").""" - __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + # Stub to indicate that arbitrary properties are accepted. # To access properties that are not valid identifiers you can use `getattr`, e.g. # `getattr(obj, '$type')` def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] class SampleInput(BaseModel): From a1493f92a7cd4399d57046aadc943aeadda5b8e7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:06:05 +0000 Subject: [PATCH 120/408] release: 1.109.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 529006a8d5..9e6e24e53d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.109.0" + ".": "1.109.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 15e8f62701..24aced9a9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.109.1 (2025-09-24) + +Full Changelog: [v1.109.0...v1.109.1](https://github.com/openai/openai-python/compare/v1.109.0...v1.109.1) + +### Bug Fixes + +* **compat:** compat with `pydantic<2.8.0` when using additional fields ([5d95ecf](https://github.com/openai/openai-python/commit/5d95ecf7abd65f3e4e273be14c80f9b4cd91ffe8)) + ## 1.109.0 (2025-09-23) Full Changelog: [v1.108.2...v1.109.0](https://github.com/openai/openai-python/compare/v1.108.2...v1.109.0) diff --git a/pyproject.toml b/pyproject.toml index 26dfbcf443..b89b4e25bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.109.0" +version = "1.109.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 16f48fb404..53c9794d8f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.109.0" # x-release-please-version +__version__ = "1.109.1" # x-release-please-version From 8b333e66c602c3e8244324c38442f21a92d214ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 30 Sep 2025 07:36:50 +0000 Subject: [PATCH 121/408] feat(api): Support images and files for function call outputs in responses, BatchUsage This release introduces a major version change to reflect a breaking modification in the `ResponseFunctionToolCallOutputItem` and `ResponseCustomToolCallOutput` schemas. Specifically, the `output` field, which previously accepted only a `string` value, has been expanded to support multiple structured types: ``` Before: output: string After: output: string | Array ``` This change allows custom tool calls to return images, files, and rich text content in addition to plain strings, aligning `ResponseCustomToolCallOutput` with the broader `ResponseInput` type system. Because this alters the type and shape of the field, it may break existing callsites that only accept strings. BREAKING CHANGE: `ResponseFunctionToolCallOutputItem.output` and `ResponseCustomToolCallOutput.output` now return `string | Array` instead of `string` only. This may break existing callsites that assume `output` is always a string. --- .stats.yml | 6 ++-- api.md | 7 +++- src/openai/types/__init__.py | 1 + src/openai/types/batch.py | 17 +++++++++ src/openai/types/batch_usage.py | 35 +++++++++++++++++++ src/openai/types/responses/__init__.py | 16 +++++++++ .../response_custom_tool_call_output.py | 21 ++++++++--- .../response_custom_tool_call_output_param.py | 18 +++++++--- ...onse_function_call_arguments_done_event.py | 3 ++ .../response_function_call_output_item.py | 16 +++++++++ ...response_function_call_output_item_list.py | 10 ++++++ ...se_function_call_output_item_list_param.py | 18 ++++++++++ ...esponse_function_call_output_item_param.py | 16 +++++++++ ...response_function_tool_call_output_item.py | 21 ++++++++--- .../responses/response_input_file_content.py | 25 +++++++++++++ .../response_input_file_content_param.py | 25 +++++++++++++ .../responses/response_input_image_content.py | 28 +++++++++++++++ .../response_input_image_content_param.py | 28 +++++++++++++++ .../types/responses/response_input_item.py | 5 +-- .../responses/response_input_item_param.py | 5 +-- .../types/responses/response_input_param.py | 5 +-- .../responses/response_input_text_content.py | 15 ++++++++ .../response_input_text_content_param.py | 15 ++++++++ 23 files changed, 332 insertions(+), 24 deletions(-) create mode 100644 src/openai/types/batch_usage.py create mode 100644 src/openai/types/responses/response_function_call_output_item.py create mode 100644 src/openai/types/responses/response_function_call_output_item_list.py create mode 100644 src/openai/types/responses/response_function_call_output_item_list_param.py create mode 100644 src/openai/types/responses/response_function_call_output_item_param.py create mode 100644 src/openai/types/responses/response_input_file_content.py create mode 100644 src/openai/types/responses/response_input_file_content_param.py create mode 100644 src/openai/types/responses/response_input_image_content.py create mode 100644 src/openai/types/responses/response_input_image_content_param.py create mode 100644 src/openai/types/responses/response_input_text_content.py create mode 100644 src/openai/types/responses/response_input_text_content_param.py diff --git a/.stats.yml b/.stats.yml index 48863a6e93..10c939b22c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-410219ea680089f02bb55163c673919703f946c3d6ad7ff5d6f607121d5287d5.yml -openapi_spec_hash: 2b3eee95d3f6796c7a61dfddf694a59a -config_hash: 666d6bb4b564f0d9d431124b5d1a0665 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-49233088b5e73dbb96bf7af27be3d4547632e3db1c2b00f14184900613325bbc.yml +openapi_spec_hash: b34f14b141d5019244112901c5c7c2d8 +config_hash: 94e9ba08201c3d1ca46e093e6a0138fa diff --git a/api.md b/api.md index 6bbb47f78c..1c80bd6e5f 100644 --- a/api.md +++ b/api.md @@ -687,7 +687,7 @@ Methods: Types: ```python -from openai.types import Batch, BatchError, BatchRequestCounts +from openai.types import Batch, BatchError, BatchRequestCounts, BatchUsage ``` Methods: @@ -769,6 +769,8 @@ from openai.types.responses import ( ResponseFormatTextJSONSchemaConfig, ResponseFunctionCallArgumentsDeltaEvent, ResponseFunctionCallArgumentsDoneEvent, + ResponseFunctionCallOutputItem, + ResponseFunctionCallOutputItemList, ResponseFunctionToolCall, ResponseFunctionToolCallItem, ResponseFunctionToolCallOutputItem, @@ -784,11 +786,14 @@ from openai.types.responses import ( ResponseInputAudio, ResponseInputContent, ResponseInputFile, + ResponseInputFileContent, ResponseInputImage, + ResponseInputImageContent, ResponseInputItem, ResponseInputMessageContentList, ResponseInputMessageItem, ResponseInputText, + ResponseInputTextContent, ResponseItem, ResponseMcpCallArgumentsDeltaEvent, ResponseMcpCallArgumentsDoneEvent, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 1844f71ba7..87e4520461 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -31,6 +31,7 @@ from .moderation import Moderation as Moderation from .audio_model import AudioModel as AudioModel from .batch_error import BatchError as BatchError +from .batch_usage import BatchUsage as BatchUsage from .file_object import FileObject as FileObject from .image_model import ImageModel as ImageModel from .file_content import FileContent as FileContent diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py index 35de90ac85..ece0513b35 100644 --- a/src/openai/types/batch.py +++ b/src/openai/types/batch.py @@ -5,6 +5,7 @@ from .._models import BaseModel from .batch_error import BatchError +from .batch_usage import BatchUsage from .shared.metadata import Metadata from .batch_request_counts import BatchRequestCounts @@ -80,8 +81,24 @@ class Batch(BaseModel): a maximum length of 512 characters. """ + model: Optional[str] = None + """Model ID used to process the batch, like `gpt-5-2025-08-07`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + output_file_id: Optional[str] = None """The ID of the file containing the outputs of successfully executed requests.""" request_counts: Optional[BatchRequestCounts] = None """The request counts for different statuses within the batch.""" + + usage: Optional[BatchUsage] = None + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. Only populated on batches + created after September 7, 2025. + """ diff --git a/src/openai/types/batch_usage.py b/src/openai/types/batch_usage.py new file mode 100644 index 0000000000..578f64a5e2 --- /dev/null +++ b/src/openai/types/batch_usage.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["BatchUsage", "InputTokensDetails", "OutputTokensDetails"] + + +class InputTokensDetails(BaseModel): + cached_tokens: int + """The number of tokens that were retrieved from the cache. + + [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching). + """ + + +class OutputTokensDetails(BaseModel): + reasoning_tokens: int + """The number of reasoning tokens.""" + + +class BatchUsage(BaseModel): + input_tokens: int + """The number of input tokens.""" + + input_tokens_details: InputTokensDetails + """A detailed breakdown of the input tokens.""" + + output_tokens: int + """The number of output tokens.""" + + output_tokens_details: OutputTokensDetails + """A detailed breakdown of the output tokens.""" + + total_tokens: int + """The total number of tokens used.""" diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index d59f0a74b8..458118741b 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -85,10 +85,13 @@ from .response_conversation_param import ResponseConversationParam as ResponseConversationParam from .response_format_text_config import ResponseFormatTextConfig as ResponseFormatTextConfig from .response_function_tool_call import ResponseFunctionToolCall as ResponseFunctionToolCall +from .response_input_file_content import ResponseInputFileContent as ResponseInputFileContent from .response_input_message_item import ResponseInputMessageItem as ResponseInputMessageItem +from .response_input_text_content import ResponseInputTextContent as ResponseInputTextContent from .response_refusal_done_event import ResponseRefusalDoneEvent as ResponseRefusalDoneEvent from .response_function_web_search import ResponseFunctionWebSearch as ResponseFunctionWebSearch from .response_input_content_param import ResponseInputContentParam as ResponseInputContentParam +from .response_input_image_content import ResponseInputImageContent as ResponseInputImageContent from .response_refusal_delta_event import ResponseRefusalDeltaEvent as ResponseRefusalDeltaEvent from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam @@ -106,8 +109,12 @@ from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent from .response_format_text_config_param import ResponseFormatTextConfigParam as ResponseFormatTextConfigParam from .response_function_tool_call_param import ResponseFunctionToolCallParam as ResponseFunctionToolCallParam +from .response_input_file_content_param import ResponseInputFileContentParam as ResponseInputFileContentParam +from .response_input_text_content_param import ResponseInputTextContentParam as ResponseInputTextContentParam from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent as ResponseMcpCallCompletedEvent +from .response_function_call_output_item import ResponseFunctionCallOutputItem as ResponseFunctionCallOutputItem from .response_function_web_search_param import ResponseFunctionWebSearchParam as ResponseFunctionWebSearchParam +from .response_input_image_content_param import ResponseInputImageContentParam as ResponseInputImageContentParam from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent as ResponseReasoningTextDoneEvent from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall as ResponseCodeInterpreterToolCall from .response_input_message_content_list import ResponseInputMessageContentList as ResponseInputMessageContentList @@ -131,6 +138,9 @@ from .response_format_text_json_schema_config import ( ResponseFormatTextJSONSchemaConfig as ResponseFormatTextJSONSchemaConfig, ) +from .response_function_call_output_item_list import ( + ResponseFunctionCallOutputItemList as ResponseFunctionCallOutputItemList, +) from .response_function_tool_call_output_item import ( ResponseFunctionToolCallOutputItem as ResponseFunctionToolCallOutputItem, ) @@ -143,6 +153,9 @@ from .response_mcp_list_tools_completed_event import ( ResponseMcpListToolsCompletedEvent as ResponseMcpListToolsCompletedEvent, ) +from .response_function_call_output_item_param import ( + ResponseFunctionCallOutputItemParam as ResponseFunctionCallOutputItemParam, +) from .response_image_gen_call_generating_event import ( ResponseImageGenCallGeneratingEvent as ResponseImageGenCallGeneratingEvent, ) @@ -212,6 +225,9 @@ from .response_format_text_json_schema_config_param import ( ResponseFormatTextJSONSchemaConfigParam as ResponseFormatTextJSONSchemaConfigParam, ) +from .response_function_call_output_item_list_param import ( + ResponseFunctionCallOutputItemListParam as ResponseFunctionCallOutputItemListParam, +) from .response_code_interpreter_call_code_done_event import ( ResponseCodeInterpreterCallCodeDoneEvent as ResponseCodeInterpreterCallCodeDoneEvent, ) diff --git a/src/openai/types/responses/response_custom_tool_call_output.py b/src/openai/types/responses/response_custom_tool_call_output.py index a2b4cc3000..9db9e7e5cf 100644 --- a/src/openai/types/responses/response_custom_tool_call_output.py +++ b/src/openai/types/responses/response_custom_tool_call_output.py @@ -1,19 +1,30 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel +from .response_input_file import ResponseInputFile +from .response_input_text import ResponseInputText +from .response_input_image import ResponseInputImage -__all__ = ["ResponseCustomToolCallOutput"] +__all__ = ["ResponseCustomToolCallOutput", "OutputOutputContentList"] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") +] class ResponseCustomToolCallOutput(BaseModel): call_id: str """The call ID, used to map this custom tool call output to a custom tool call.""" - output: str - """The output from the custom tool call generated by your code.""" + output: Union[str, List[OutputOutputContentList]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ type: Literal["custom_tool_call_output"] """The type of the custom tool call output. Always `custom_tool_call_output`.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output_param.py b/src/openai/types/responses/response_custom_tool_call_output_param.py index d52c525467..e967a37cff 100644 --- a/src/openai/types/responses/response_custom_tool_call_output_param.py +++ b/src/openai/types/responses/response_custom_tool_call_output_param.py @@ -2,17 +2,27 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["ResponseCustomToolCallOutputParam"] +from .response_input_file_param import ResponseInputFileParam +from .response_input_text_param import ResponseInputTextParam +from .response_input_image_param import ResponseInputImageParam + +__all__ = ["ResponseCustomToolCallOutputParam", "OutputOutputContentList"] + +OutputOutputContentList: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] class ResponseCustomToolCallOutputParam(TypedDict, total=False): call_id: Required[str] """The call ID, used to map this custom tool call output to a custom tool call.""" - output: Required[str] - """The output from the custom tool call generated by your code.""" + output: Required[Union[str, Iterable[OutputOutputContentList]]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ type: Required[Literal["custom_tool_call_output"]] """The type of the custom tool call output. Always `custom_tool_call_output`.""" diff --git a/src/openai/types/responses/response_function_call_arguments_done_event.py b/src/openai/types/responses/response_function_call_arguments_done_event.py index 875e7a6875..4ee5ed7fe1 100644 --- a/src/openai/types/responses/response_function_call_arguments_done_event.py +++ b/src/openai/types/responses/response_function_call_arguments_done_event.py @@ -14,6 +14,9 @@ class ResponseFunctionCallArgumentsDoneEvent(BaseModel): item_id: str """The ID of the item.""" + name: str + """The name of the function that was called.""" + output_index: int """The index of the output item.""" diff --git a/src/openai/types/responses/response_function_call_output_item.py b/src/openai/types/responses/response_function_call_output_item.py new file mode 100644 index 0000000000..41898f9eda --- /dev/null +++ b/src/openai/types/responses/response_function_call_output_item.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_input_file_content import ResponseInputFileContent +from .response_input_text_content import ResponseInputTextContent +from .response_input_image_content import ResponseInputImageContent + +__all__ = ["ResponseFunctionCallOutputItem"] + +ResponseFunctionCallOutputItem: TypeAlias = Annotated[ + Union[ResponseInputTextContent, ResponseInputImageContent, ResponseInputFileContent], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/responses/response_function_call_output_item_list.py b/src/openai/types/responses/response_function_call_output_item_list.py new file mode 100644 index 0000000000..13db577160 --- /dev/null +++ b/src/openai/types/responses/response_function_call_output_item_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .response_function_call_output_item import ResponseFunctionCallOutputItem + +__all__ = ["ResponseFunctionCallOutputItemList"] + +ResponseFunctionCallOutputItemList: TypeAlias = List[ResponseFunctionCallOutputItem] diff --git a/src/openai/types/responses/response_function_call_output_item_list_param.py b/src/openai/types/responses/response_function_call_output_item_list_param.py new file mode 100644 index 0000000000..8c286d3cf0 --- /dev/null +++ b/src/openai/types/responses/response_function_call_output_item_list_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .response_input_file_content_param import ResponseInputFileContentParam +from .response_input_text_content_param import ResponseInputTextContentParam +from .response_input_image_content_param import ResponseInputImageContentParam + +__all__ = ["ResponseFunctionCallOutputItemListParam", "ResponseFunctionCallOutputItemParam"] + +ResponseFunctionCallOutputItemParam: TypeAlias = Union[ + ResponseInputTextContentParam, ResponseInputImageContentParam, ResponseInputFileContentParam +] + +ResponseFunctionCallOutputItemListParam: TypeAlias = List[ResponseFunctionCallOutputItemParam] diff --git a/src/openai/types/responses/response_function_call_output_item_param.py b/src/openai/types/responses/response_function_call_output_item_param.py new file mode 100644 index 0000000000..2a703cac1e --- /dev/null +++ b/src/openai/types/responses/response_function_call_output_item_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .response_input_file_content_param import ResponseInputFileContentParam +from .response_input_text_content_param import ResponseInputTextContentParam +from .response_input_image_content_param import ResponseInputImageContentParam + +__all__ = ["ResponseFunctionCallOutputItemParam"] + +ResponseFunctionCallOutputItemParam: TypeAlias = Union[ + ResponseInputTextContentParam, ResponseInputImageContentParam, ResponseInputFileContentParam +] diff --git a/src/openai/types/responses/response_function_tool_call_output_item.py b/src/openai/types/responses/response_function_tool_call_output_item.py index 4c8c41a6fe..1a2c848cb3 100644 --- a/src/openai/types/responses/response_function_tool_call_output_item.py +++ b/src/openai/types/responses/response_function_tool_call_output_item.py @@ -1,11 +1,19 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel +from .response_input_file import ResponseInputFile +from .response_input_text import ResponseInputText +from .response_input_image import ResponseInputImage -__all__ = ["ResponseFunctionToolCallOutputItem"] +__all__ = ["ResponseFunctionToolCallOutputItem", "OutputOutputContentList"] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") +] class ResponseFunctionToolCallOutputItem(BaseModel): @@ -15,8 +23,11 @@ class ResponseFunctionToolCallOutputItem(BaseModel): call_id: str """The unique ID of the function tool call generated by the model.""" - output: str - """A JSON string of the output of the function tool call.""" + output: Union[str, List[OutputOutputContentList]] + """ + The output from the function call generated by your code. Can be a string or an + list of output content. + """ type: Literal["function_call_output"] """The type of the function tool call output. Always `function_call_output`.""" diff --git a/src/openai/types/responses/response_input_file_content.py b/src/openai/types/responses/response_input_file_content.py new file mode 100644 index 0000000000..d832bb0e26 --- /dev/null +++ b/src/openai/types/responses/response_input_file_content.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputFileContent"] + + +class ResponseInputFileContent(BaseModel): + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + file_data: Optional[str] = None + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_content_param.py b/src/openai/types/responses/response_input_file_content_param.py new file mode 100644 index 0000000000..71f7b3a281 --- /dev/null +++ b/src/openai/types/responses/response_input_file_content_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputFileContentParam"] + + +class ResponseInputFileContentParam(TypedDict, total=False): + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + file_data: Optional[str] + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] + """The URL of the file to be sent to the model.""" + + filename: Optional[str] + """The name of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_image_content.py b/src/openai/types/responses/response_input_image_content.py new file mode 100644 index 0000000000..fb90cb57eb --- /dev/null +++ b/src/openai/types/responses/response_input_image_content.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputImageContent"] + + +class ResponseInputImageContent(BaseModel): + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + detail: Optional[Literal["low", "high", "auto"]] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ diff --git a/src/openai/types/responses/response_input_image_content_param.py b/src/openai/types/responses/response_input_image_content_param.py new file mode 100644 index 0000000000..c51509a3f3 --- /dev/null +++ b/src/openai/types/responses/response_input_image_content_param.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputImageContentParam"] + + +class ResponseInputImageContentParam(TypedDict, total=False): + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + detail: Optional[Literal["low", "high", "auto"]] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index d2b454fd2c..b8358cbee1 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -16,6 +16,7 @@ from .response_custom_tool_call_output import ResponseCustomToolCallOutput from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_input_message_content_list import ResponseInputMessageContentList +from .response_function_call_output_item_list import ResponseFunctionCallOutputItemList from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot __all__ = [ @@ -100,8 +101,8 @@ class FunctionCallOutput(BaseModel): call_id: str """The unique ID of the function tool call generated by the model.""" - output: str - """A JSON string of the output of the function tool call.""" + output: Union[str, ResponseFunctionCallOutputItemList] + """Text, image, or file output of the function tool call.""" type: Literal["function_call_output"] """The type of the function tool call output. Always `function_call_output`.""" diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 5ad83fc03a..13832cc3f2 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -17,6 +17,7 @@ from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam __all__ = [ @@ -101,8 +102,8 @@ class FunctionCallOutput(TypedDict, total=False): call_id: Required[str] """The unique ID of the function tool call generated by the model.""" - output: Required[str] - """A JSON string of the output of the function tool call.""" + output: Required[Union[str, ResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" type: Required[Literal["function_call_output"]] """The type of the function tool call output. Always `function_call_output`.""" diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 73eac62428..aa1f639e9c 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -17,6 +17,7 @@ from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam __all__ = [ @@ -102,8 +103,8 @@ class FunctionCallOutput(TypedDict, total=False): call_id: Required[str] """The unique ID of the function tool call generated by the model.""" - output: Required[str] - """A JSON string of the output of the function tool call.""" + output: Required[Union[str, ResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" type: Required[Literal["function_call_output"]] """The type of the function tool call output. Always `function_call_output`.""" diff --git a/src/openai/types/responses/response_input_text_content.py b/src/openai/types/responses/response_input_text_content.py new file mode 100644 index 0000000000..2cce849855 --- /dev/null +++ b/src/openai/types/responses/response_input_text_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseInputTextContent"] + + +class ResponseInputTextContent(BaseModel): + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" diff --git a/src/openai/types/responses/response_input_text_content_param.py b/src/openai/types/responses/response_input_text_content_param.py new file mode 100644 index 0000000000..85b57df2bd --- /dev/null +++ b/src/openai/types/responses/response_input_text_content_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseInputTextContentParam"] + + +class ResponseInputTextContentParam(TypedDict, total=False): + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" From 70650697e6d2dddc841e0e44515e35829ee3637a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 30 Sep 2025 07:37:22 +0000 Subject: [PATCH 122/408] release: 2.0.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 12 ++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9e6e24e53d..65f558e71b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.109.1" + ".": "2.0.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 24aced9a9d..0e71b12205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 2.0.0 (2025-09-30) + +Full Changelog: [v1.109.1...v2.0.0](https://github.com/openai/openai-python/compare/v1.109.1...v2.0.0) + +### ⚠ BREAKING CHANGES + +* **api:** `ResponseFunctionToolCallOutputItem.output` and `ResponseCustomToolCallOutput.output` now return `string | Array` instead of `string` only. This may break existing callsites that assume `output` is always a string. + +### Features + +* **api:** Support images and files for function call outputs in responses, BatchUsage ([4105376](https://github.com/openai/openai-python/commit/4105376a60293581371fd5635b805b717d24aa19)) + ## 1.109.1 (2025-09-24) Full Changelog: [v1.109.0...v1.109.1](https://github.com/openai/openai-python/compare/v1.109.0...v1.109.1) diff --git a/pyproject.toml b/pyproject.toml index b89b4e25bd..aadde38c02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "1.109.1" +version = "2.0.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 53c9794d8f..d34c028c6a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "1.109.1" # x-release-please-version +__version__ = "2.0.0" # x-release-please-version From d1e24895c537ea2d3aa00179dc05f8b512be7e8c Mon Sep 17 00:00:00 2001 From: Alex Gamble Date: Wed, 1 Oct 2025 14:23:44 +0100 Subject: [PATCH 123/408] [realtime] Add gpt-realtime models to beta SDK interface --- src/openai/resources/beta/realtime/sessions.py | 4 ++++ src/openai/types/beta/realtime/session.py | 2 ++ src/openai/types/beta/realtime/session_create_params.py | 2 ++ src/openai/types/beta/realtime/session_update_event.py | 2 ++ src/openai/types/beta/realtime/session_update_event_param.py | 2 ++ 5 files changed, 12 insertions(+) diff --git a/src/openai/resources/beta/realtime/sessions.py b/src/openai/resources/beta/realtime/sessions.py index eaddb384ce..9b85e02d17 100644 --- a/src/openai/resources/beta/realtime/sessions.py +++ b/src/openai/resources/beta/realtime/sessions.py @@ -51,6 +51,8 @@ def create( max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", @@ -233,6 +235,8 @@ async def create( max_response_output_tokens: Union[int, Literal["inf"]] | NotGiven = NOT_GIVEN, modalities: List[Literal["text", "audio"]] | NotGiven = NOT_GIVEN, model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", diff --git a/src/openai/types/beta/realtime/session.py b/src/openai/types/beta/realtime/session.py index f478a92fbb..2e099a2e98 100644 --- a/src/openai/types/beta/realtime/session.py +++ b/src/openai/types/beta/realtime/session.py @@ -203,6 +203,8 @@ class Session(BaseModel): model: Optional[ Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", diff --git a/src/openai/types/beta/realtime/session_create_params.py b/src/openai/types/beta/realtime/session_create_params.py index 8a477f9843..38465a56c3 100644 --- a/src/openai/types/beta/realtime/session_create_params.py +++ b/src/openai/types/beta/realtime/session_create_params.py @@ -81,6 +81,8 @@ class SessionCreateParams(TypedDict, total=False): """ model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", diff --git a/src/openai/types/beta/realtime/session_update_event.py b/src/openai/types/beta/realtime/session_update_event.py index 11929ab376..78d2e4bb18 100644 --- a/src/openai/types/beta/realtime/session_update_event.py +++ b/src/openai/types/beta/realtime/session_update_event.py @@ -225,6 +225,8 @@ class Session(BaseModel): model: Optional[ Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", diff --git a/src/openai/types/beta/realtime/session_update_event_param.py b/src/openai/types/beta/realtime/session_update_event_param.py index e939f4cc79..c58b202a71 100644 --- a/src/openai/types/beta/realtime/session_update_event_param.py +++ b/src/openai/types/beta/realtime/session_update_event_param.py @@ -224,6 +224,8 @@ class Session(TypedDict, total=False): """ model: Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", "gpt-4o-realtime-preview-2024-12-17", From d5e79998b0017e77ca48b470c70ed7679693fd91 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:45:25 +0000 Subject: [PATCH 124/408] fix(api): add status, approval_request_id to MCP tool call --- .stats.yml | 6 +++--- src/openai/types/conversations/conversation_item.py | 13 +++++++++++++ src/openai/types/responses/response_input_item.py | 13 +++++++++++++ .../types/responses/response_input_item_param.py | 13 +++++++++++++ src/openai/types/responses/response_input_param.py | 13 +++++++++++++ src/openai/types/responses/response_item.py | 13 +++++++++++++ src/openai/types/responses/response_output_item.py | 13 +++++++++++++ 7 files changed, 81 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 10c939b22c..27f2ffc6db 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-49233088b5e73dbb96bf7af27be3d4547632e3db1c2b00f14184900613325bbc.yml -openapi_spec_hash: b34f14b141d5019244112901c5c7c2d8 -config_hash: 94e9ba08201c3d1ca46e093e6a0138fa +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-e205b1f2da6a1f2caa229efa9ede63f2d3d2fedeeb2dd6ed3d880bafdcb0ab88.yml +openapi_spec_hash: c8aee2469a749f6a838b40c57e4b7b06 +config_hash: 45dcba51451ba532959c020a0ddbf23c diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py index a7cd355f36..9e9fb40033 100644 --- a/src/openai/types/conversations/conversation_item.py +++ b/src/openai/types/conversations/conversation_item.py @@ -177,12 +177,25 @@ class McpCall(BaseModel): type: Literal["mcp_call"] """The type of the item. Always `mcp_call`.""" + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + error: Optional[str] = None """The error from the tool call, if any.""" output: Optional[str] = None """The output from the tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + ConversationItem: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index b8358cbee1..0a487b8bef 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -267,12 +267,25 @@ class McpCall(BaseModel): type: Literal["mcp_call"] """The type of the item. Always `mcp_call`.""" + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + error: Optional[str] = None """The error from the tool call, if any.""" output: Optional[str] = None """The output from the tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + class ItemReference(BaseModel): id: str diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 13832cc3f2..115147dc4b 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -268,12 +268,25 @@ class McpCall(TypedDict, total=False): type: Required[Literal["mcp_call"]] """The type of the item. Always `mcp_call`.""" + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + error: Optional[str] """The error from the tool call, if any.""" output: Optional[str] """The output from the tool call.""" + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + class ItemReference(TypedDict, total=False): id: Required[str] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index aa1f639e9c..9a999c7252 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -269,12 +269,25 @@ class McpCall(TypedDict, total=False): type: Required[Literal["mcp_call"]] """The type of the item. Always `mcp_call`.""" + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + error: Optional[str] """The error from the tool call, if any.""" output: Optional[str] """The output from the tool call.""" + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + class ItemReference(TypedDict, total=False): id: Required[str] diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py index cba89390ed..bdd2523baf 100644 --- a/src/openai/types/responses/response_item.py +++ b/src/openai/types/responses/response_item.py @@ -175,12 +175,25 @@ class McpCall(BaseModel): type: Literal["mcp_call"] """The type of the item. Always `mcp_call`.""" + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + error: Optional[str] = None """The error from the tool call, if any.""" output: Optional[str] = None """The output from the tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + ResponseItem: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index 2d3ee7b64e..e33d59cefe 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -93,12 +93,25 @@ class McpCall(BaseModel): type: Literal["mcp_call"] """The type of the item. Always `mcp_call`.""" + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + error: Optional[str] = None """The error from the tool call, if any.""" output: Optional[str] = None """The output from the tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + class McpListToolsTool(BaseModel): input_schema: object From 75a3aa490a6bf8e91ec98df12590980a680ac77a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:45:55 +0000 Subject: [PATCH 125/408] release: 2.0.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 65f558e71b..cf7239890c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.0.0" + ".": "2.0.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e71b12205..fcb9926c29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.0.1 (2025-10-01) + +Full Changelog: [v2.0.0...v2.0.1](https://github.com/openai/openai-python/compare/v2.0.0...v2.0.1) + +### Bug Fixes + +* **api:** add status, approval_request_id to MCP tool call ([2a02255](https://github.com/openai/openai-python/commit/2a022553f83b636defcfda3b1c6f4b12d901357b)) + ## 2.0.0 (2025-09-30) Full Changelog: [v1.109.1...v2.0.0](https://github.com/openai/openai-python/compare/v1.109.1...v2.0.0) diff --git a/pyproject.toml b/pyproject.toml index aadde38c02..a83bca2dcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.0.0" +version = "2.0.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index d34c028c6a..f20794ac19 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.0.0" # x-release-please-version +__version__ = "2.0.1" # x-release-please-version From 86aaa1de28cdc4b186baf440b370684a05e8ae95 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 20:13:26 +0000 Subject: [PATCH 126/408] feat(api): add support for realtime calls --- .stats.yml | 8 +- api.md | 10 + src/openai/lib/_realtime.py | 92 +++ src/openai/resources/realtime/__init__.py | 14 + src/openai/resources/realtime/calls.py | 734 ++++++++++++++++++ src/openai/resources/realtime/realtime.py | 70 +- src/openai/types/realtime/__init__.py | 4 + .../types/realtime/call_accept_params.py | 107 +++ .../types/realtime/call_create_params.py | 17 + .../types/realtime/call_refer_params.py | 15 + .../types/realtime/call_reject_params.py | 15 + .../types/realtime/realtime_connect_params.py | 6 +- tests/api_resources/realtime/test_calls.py | 692 +++++++++++++++++ 13 files changed, 1770 insertions(+), 14 deletions(-) create mode 100644 src/openai/lib/_realtime.py create mode 100644 src/openai/resources/realtime/calls.py create mode 100644 src/openai/types/realtime/call_accept_params.py create mode 100644 src/openai/types/realtime/call_create_params.py create mode 100644 src/openai/types/realtime/call_refer_params.py create mode 100644 src/openai/types/realtime/call_reject_params.py create mode 100644 tests/api_resources/realtime/test_calls.py diff --git a/.stats.yml b/.stats.yml index 27f2ffc6db..d974760a99 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-e205b1f2da6a1f2caa229efa9ede63f2d3d2fedeeb2dd6ed3d880bafdcb0ab88.yml -openapi_spec_hash: c8aee2469a749f6a838b40c57e4b7b06 -config_hash: 45dcba51451ba532959c020a0ddbf23c +configured_endpoints: 123 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fadefdc7c7e30df47c09df323669b242ff90ee08e51f304175ace5274e0aab49.yml +openapi_spec_hash: 6d20f639d9ff8a097a34962da6218231 +config_hash: 902654e60f5d659f2bfcfd903e17c46d diff --git a/api.md b/api.md index 1c80bd6e5f..d5331803ce 100644 --- a/api.md +++ b/api.md @@ -989,6 +989,16 @@ Methods: - client.realtime.client_secrets.create(\*\*params) -> ClientSecretCreateResponse +## Calls + +Methods: + +- client.realtime.calls.create(\*\*params) -> HttpxBinaryResponseContent +- client.realtime.calls.accept(call_id, \*\*params) -> None +- client.realtime.calls.hangup(call_id) -> None +- client.realtime.calls.refer(call_id, \*\*params) -> None +- client.realtime.calls.reject(call_id, \*\*params) -> None + # Conversations Types: diff --git a/src/openai/lib/_realtime.py b/src/openai/lib/_realtime.py new file mode 100644 index 0000000000..999d1e4463 --- /dev/null +++ b/src/openai/lib/_realtime.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import json +from typing_extensions import override + +import httpx + +from openai import _legacy_response +from openai._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from openai._utils import maybe_transform, async_maybe_transform +from openai._base_client import make_request_options +from openai.resources.realtime.calls import Calls, AsyncCalls +from openai.types.realtime.realtime_session_create_request_param import RealtimeSessionCreateRequestParam + +__all__ = ["_Calls", "_AsyncCalls"] + + +# Custom code to override the `create` method to have correct behavior with +# application/sdp and multipart/form-data. +# Ideally we can cutover to the generated code this overrides eventually and remove this. +class _Calls(Calls): + @override + def create( + self, + *, + sdp: str, + session: RealtimeSessionCreateRequestParam | Omit = omit, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + if session is omit: + extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})} + return self._post( + "/realtime/calls", + body=sdp.encode("utf-8"), + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, timeout=timeout), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + extra_headers = {"Accept": "application/sdp", "Content-Type": "multipart/form-data", **(extra_headers or {})} + session_payload = maybe_transform(session, RealtimeSessionCreateRequestParam) + files = [ + ("sdp", (None, sdp.encode("utf-8"), "application/sdp")), + ("session", (None, json.dumps(session_payload).encode("utf-8"), "application/json")), + ] + return self._post( + "/realtime/calls", + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class _AsyncCalls(AsyncCalls): + @override + async def create( + self, + *, + sdp: str, + session: RealtimeSessionCreateRequestParam | Omit = omit, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + if session is omit: + extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})} + return await self._post( + "/realtime/calls", + body=sdp.encode("utf-8"), + options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, timeout=timeout), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + extra_headers = {"Accept": "application/sdp", "Content-Type": "multipart/form-data", **(extra_headers or {})} + session_payload = await async_maybe_transform(session, RealtimeSessionCreateRequestParam) + files = [ + ("sdp", (None, sdp.encode("utf-8"), "application/sdp")), + ("session", (None, json.dumps(session_payload).encode("utf-8"), "application/json")), + ] + return await self._post( + "/realtime/calls", + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) diff --git a/src/openai/resources/realtime/__init__.py b/src/openai/resources/realtime/__init__.py index 7a41de8648..c11841017f 100644 --- a/src/openai/resources/realtime/__init__.py +++ b/src/openai/resources/realtime/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .calls import ( + Calls, + AsyncCalls, + CallsWithRawResponse, + AsyncCallsWithRawResponse, + CallsWithStreamingResponse, + AsyncCallsWithStreamingResponse, +) from .realtime import ( Realtime, AsyncRealtime, @@ -24,6 +32,12 @@ "AsyncClientSecretsWithRawResponse", "ClientSecretsWithStreamingResponse", "AsyncClientSecretsWithStreamingResponse", + "Calls", + "AsyncCalls", + "CallsWithRawResponse", + "AsyncCallsWithRawResponse", + "CallsWithStreamingResponse", + "AsyncCallsWithStreamingResponse", "Realtime", "AsyncRealtime", "RealtimeWithRawResponse", diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py new file mode 100644 index 0000000000..7dcea6b5cf --- /dev/null +++ b/src/openai/resources/realtime/calls.py @@ -0,0 +1,734 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal + +import httpx + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.realtime import ( + call_refer_params, + call_accept_params, + call_create_params, + call_reject_params, +) +from ...types.responses.response_prompt_param import ResponsePromptParam +from ...types.realtime.realtime_truncation_param import RealtimeTruncationParam +from ...types.realtime.realtime_audio_config_param import RealtimeAudioConfigParam +from ...types.realtime.realtime_tools_config_param import RealtimeToolsConfigParam +from ...types.realtime.realtime_tracing_config_param import RealtimeTracingConfigParam +from ...types.realtime.realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam +from ...types.realtime.realtime_session_create_request_param import RealtimeSessionCreateRequestParam + +__all__ = ["Calls", "AsyncCalls"] + + +class Calls(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CallsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CallsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CallsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CallsWithStreamingResponse(self) + + def create( + self, + *, + sdp: str, + session: RealtimeSessionCreateRequestParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Create a new Realtime API call over WebRTC and receive the SDP answer needed to + complete the peer connection. + + Args: + sdp: WebRTC Session Description Protocol (SDP) offer generated by the caller. + + session: Realtime session object configuration. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/sdp", **(extra_headers or {})} + return self._post( + "/realtime/calls", + body=maybe_transform( + { + "sdp": sdp, + "session": session, + }, + call_create_params.CallCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + def accept( + self, + call_id: str, + *, + type: Literal["realtime"], + audio: RealtimeAudioConfigParam | Omit = omit, + include: List[Literal["item.input_audio_transcription.logprobs"]] | Omit = omit, + instructions: str | Omit = omit, + max_output_tokens: Union[int, Literal["inf"]] | Omit = omit, + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + ] + | Omit = omit, + output_modalities: List[Literal["text", "audio"]] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + tool_choice: RealtimeToolChoiceConfigParam | Omit = omit, + tools: RealtimeToolsConfigParam | Omit = omit, + tracing: Optional[RealtimeTracingConfigParam] | Omit = omit, + truncation: RealtimeTruncationParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Accept an incoming SIP call and configure the realtime session that will handle + it. + + Args: + type: The type of session to create. Always `realtime` for the Realtime API. + + audio: Configuration for input and output audio. + + include: Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + model: The Realtime model used for this session. + + output_modalities: The set of modalities the model can respond with. It defaults to `["audio"]`, + indicating that the model will respond with audio plus a transcript. `["text"]` + can be used to make the model respond with text only. It is not possible to + request both `text` and `audio` at the same time. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + tool_choice: How the model chooses tools. Provide one of the string modes or force a specific + function/MCP tool. + + tools: Tools available to the model. + + tracing: Realtime API can write session traces to the + [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once + tracing is enabled for a session, the configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + + truncation: Controls how the realtime conversation is truncated prior to model inference. + The default is `auto`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/realtime/calls/{call_id}/accept", + body=maybe_transform( + { + "type": type, + "audio": audio, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "model": model, + "output_modalities": output_modalities, + "prompt": prompt, + "tool_choice": tool_choice, + "tools": tools, + "tracing": tracing, + "truncation": truncation, + }, + call_accept_params.CallAcceptParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def hangup( + self, + call_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + End an active Realtime API call, whether it was initiated over SIP or WebRTC. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/realtime/calls/{call_id}/hangup", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def refer( + self, + call_id: str, + *, + target_uri: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Transfer an active SIP call to a new destination using the SIP REFER verb. + + Args: + target_uri: URI that should appear in the SIP Refer-To header. Supports values like + `tel:+14155550123` or `sip:agent@example.com`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/realtime/calls/{call_id}/refer", + body=maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def reject( + self, + call_id: str, + *, + status_code: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Decline an incoming SIP call by returning a SIP status code to the caller. + + Args: + status_code: SIP response code to send back to the caller. Defaults to `603` (Decline) when + omitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._post( + f"/realtime/calls/{call_id}/reject", + body=maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncCalls(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCallsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCallsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCallsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCallsWithStreamingResponse(self) + + async def create( + self, + *, + sdp: str, + session: RealtimeSessionCreateRequestParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Create a new Realtime API call over WebRTC and receive the SDP answer needed to + complete the peer connection. + + Args: + sdp: WebRTC Session Description Protocol (SDP) offer generated by the caller. + + session: Realtime session object configuration. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "application/sdp", **(extra_headers or {})} + return await self._post( + "/realtime/calls", + body=await async_maybe_transform( + { + "sdp": sdp, + "session": session, + }, + call_create_params.CallCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + async def accept( + self, + call_id: str, + *, + type: Literal["realtime"], + audio: RealtimeAudioConfigParam | Omit = omit, + include: List[Literal["item.input_audio_transcription.logprobs"]] | Omit = omit, + instructions: str | Omit = omit, + max_output_tokens: Union[int, Literal["inf"]] | Omit = omit, + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + ] + | Omit = omit, + output_modalities: List[Literal["text", "audio"]] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + tool_choice: RealtimeToolChoiceConfigParam | Omit = omit, + tools: RealtimeToolsConfigParam | Omit = omit, + tracing: Optional[RealtimeTracingConfigParam] | Omit = omit, + truncation: RealtimeTruncationParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Accept an incoming SIP call and configure the realtime session that will handle + it. + + Args: + type: The type of session to create. Always `realtime` for the Realtime API. + + audio: Configuration for input and output audio. + + include: Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + + instructions: The default system instructions (i.e. system message) prepended to model calls. + This field allows the client to guide the model on desired responses. The model + can be instructed on response content and format, (e.g. "be extremely succinct", + "act friendly", "here are examples of good responses") and on audio behavior + (e.g. "talk quickly", "inject emotion into your voice", "laugh frequently"). The + instructions are not guaranteed to be followed by the model, but they provide + guidance to the model on the desired behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + + max_output_tokens: Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + + model: The Realtime model used for this session. + + output_modalities: The set of modalities the model can respond with. It defaults to `["audio"]`, + indicating that the model will respond with audio plus a transcript. `["text"]` + can be used to make the model respond with text only. It is not possible to + request both `text` and `audio` at the same time. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + tool_choice: How the model chooses tools. Provide one of the string modes or force a specific + function/MCP tool. + + tools: Tools available to the model. + + tracing: Realtime API can write session traces to the + [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once + tracing is enabled for a session, the configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + + truncation: Controls how the realtime conversation is truncated prior to model inference. + The default is `auto`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/realtime/calls/{call_id}/accept", + body=await async_maybe_transform( + { + "type": type, + "audio": audio, + "include": include, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "model": model, + "output_modalities": output_modalities, + "prompt": prompt, + "tool_choice": tool_choice, + "tools": tools, + "tracing": tracing, + "truncation": truncation, + }, + call_accept_params.CallAcceptParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def hangup( + self, + call_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + End an active Realtime API call, whether it was initiated over SIP or WebRTC. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/realtime/calls/{call_id}/hangup", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def refer( + self, + call_id: str, + *, + target_uri: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Transfer an active SIP call to a new destination using the SIP REFER verb. + + Args: + target_uri: URI that should appear in the SIP Refer-To header. Supports values like + `tel:+14155550123` or `sip:agent@example.com`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/realtime/calls/{call_id}/refer", + body=await async_maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def reject( + self, + call_id: str, + *, + status_code: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Decline an incoming SIP call by returning a SIP status code to the caller. + + Args: + status_code: SIP response code to send back to the caller. Defaults to `603` (Decline) when + omitted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not call_id: + raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._post( + f"/realtime/calls/{call_id}/reject", + body=await async_maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class CallsWithRawResponse: + def __init__(self, calls: Calls) -> None: + self._calls = calls + + self.create = _legacy_response.to_raw_response_wrapper( + calls.create, + ) + self.accept = _legacy_response.to_raw_response_wrapper( + calls.accept, + ) + self.hangup = _legacy_response.to_raw_response_wrapper( + calls.hangup, + ) + self.refer = _legacy_response.to_raw_response_wrapper( + calls.refer, + ) + self.reject = _legacy_response.to_raw_response_wrapper( + calls.reject, + ) + + +class AsyncCallsWithRawResponse: + def __init__(self, calls: AsyncCalls) -> None: + self._calls = calls + + self.create = _legacy_response.async_to_raw_response_wrapper( + calls.create, + ) + self.accept = _legacy_response.async_to_raw_response_wrapper( + calls.accept, + ) + self.hangup = _legacy_response.async_to_raw_response_wrapper( + calls.hangup, + ) + self.refer = _legacy_response.async_to_raw_response_wrapper( + calls.refer, + ) + self.reject = _legacy_response.async_to_raw_response_wrapper( + calls.reject, + ) + + +class CallsWithStreamingResponse: + def __init__(self, calls: Calls) -> None: + self._calls = calls + + self.create = to_custom_streamed_response_wrapper( + calls.create, + StreamedBinaryAPIResponse, + ) + self.accept = to_streamed_response_wrapper( + calls.accept, + ) + self.hangup = to_streamed_response_wrapper( + calls.hangup, + ) + self.refer = to_streamed_response_wrapper( + calls.refer, + ) + self.reject = to_streamed_response_wrapper( + calls.reject, + ) + + +class AsyncCallsWithStreamingResponse: + def __init__(self, calls: AsyncCalls) -> None: + self._calls = calls + + self.create = async_to_custom_streamed_response_wrapper( + calls.create, + AsyncStreamedBinaryAPIResponse, + ) + self.accept = async_to_streamed_response_wrapper( + calls.accept, + ) + self.hangup = async_to_streamed_response_wrapper( + calls.hangup, + ) + self.refer = async_to_streamed_response_wrapper( + calls.refer, + ) + self.reject = async_to_streamed_response_wrapper( + calls.reject, + ) diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 9d61fa25e0..7b4486d502 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -11,6 +11,14 @@ import httpx from pydantic import BaseModel +from .calls import ( + Calls, + AsyncCalls, + CallsWithRawResponse, + AsyncCallsWithRawResponse, + CallsWithStreamingResponse, + AsyncCallsWithStreamingResponse, +) from ..._types import Omit, Query, Headers, omit from ..._utils import ( is_azure_client, @@ -56,6 +64,11 @@ class Realtime(SyncAPIResource): def client_secrets(self) -> ClientSecrets: return ClientSecrets(self._client) + @cached_property + def calls(self) -> Calls: + from ...lib._realtime import _Calls + return _Calls(self._client) + @cached_property def with_raw_response(self) -> RealtimeWithRawResponse: """ @@ -78,7 +91,8 @@ def with_streaming_response(self) -> RealtimeWithStreamingResponse: def connect( self, *, - model: str, + call_id: str | Omit = omit, + model: str | Omit = omit, extra_query: Query = {}, extra_headers: Headers = {}, websocket_connection_options: WebsocketConnectionOptions = {}, @@ -99,6 +113,7 @@ def connect( extra_query=extra_query, extra_headers=extra_headers, websocket_connection_options=websocket_connection_options, + call_id=call_id, model=model, ) @@ -108,6 +123,11 @@ class AsyncRealtime(AsyncAPIResource): def client_secrets(self) -> AsyncClientSecrets: return AsyncClientSecrets(self._client) + @cached_property + def calls(self) -> AsyncCalls: + from ...lib._realtime import _AsyncCalls + return _AsyncCalls(self._client) + @cached_property def with_raw_response(self) -> AsyncRealtimeWithRawResponse: """ @@ -130,7 +150,8 @@ def with_streaming_response(self) -> AsyncRealtimeWithStreamingResponse: def connect( self, *, - model: str, + call_id: str | Omit = omit, + model: str | Omit = omit, extra_query: Query = {}, extra_headers: Headers = {}, websocket_connection_options: WebsocketConnectionOptions = {}, @@ -151,6 +172,7 @@ def connect( extra_query=extra_query, extra_headers=extra_headers, websocket_connection_options=websocket_connection_options, + call_id=call_id, model=model, ) @@ -163,6 +185,10 @@ def __init__(self, realtime: Realtime) -> None: def client_secrets(self) -> ClientSecretsWithRawResponse: return ClientSecretsWithRawResponse(self._realtime.client_secrets) + @cached_property + def calls(self) -> CallsWithRawResponse: + return CallsWithRawResponse(self._realtime.calls) + class AsyncRealtimeWithRawResponse: def __init__(self, realtime: AsyncRealtime) -> None: @@ -172,6 +198,10 @@ def __init__(self, realtime: AsyncRealtime) -> None: def client_secrets(self) -> AsyncClientSecretsWithRawResponse: return AsyncClientSecretsWithRawResponse(self._realtime.client_secrets) + @cached_property + def calls(self) -> AsyncCallsWithRawResponse: + return AsyncCallsWithRawResponse(self._realtime.calls) + class RealtimeWithStreamingResponse: def __init__(self, realtime: Realtime) -> None: @@ -181,6 +211,10 @@ def __init__(self, realtime: Realtime) -> None: def client_secrets(self) -> ClientSecretsWithStreamingResponse: return ClientSecretsWithStreamingResponse(self._realtime.client_secrets) + @cached_property + def calls(self) -> CallsWithStreamingResponse: + return CallsWithStreamingResponse(self._realtime.calls) + class AsyncRealtimeWithStreamingResponse: def __init__(self, realtime: AsyncRealtime) -> None: @@ -190,6 +224,10 @@ def __init__(self, realtime: AsyncRealtime) -> None: def client_secrets(self) -> AsyncClientSecretsWithStreamingResponse: return AsyncClientSecretsWithStreamingResponse(self._realtime.client_secrets) + @cached_property + def calls(self) -> AsyncCallsWithStreamingResponse: + return AsyncCallsWithStreamingResponse(self._realtime.calls) + class AsyncRealtimeConnection: """Represents a live websocket connection to the Realtime API""" @@ -290,12 +328,14 @@ def __init__( self, *, client: AsyncOpenAI, - model: str, + call_id: str | Omit = omit, + model: str | Omit = omit, extra_query: Query, extra_headers: Headers, websocket_connection_options: WebsocketConnectionOptions, ) -> None: self.__client = client + self.__call_id = call_id self.__model = model self.__connection: AsyncRealtimeConnection | None = None self.__extra_query = extra_query @@ -323,13 +363,19 @@ async def __aenter__(self) -> AsyncRealtimeConnection: extra_query = self.__extra_query await self.__client._refresh_api_key() auth_headers = self.__client.auth_headers + if self.__call_id is not omit: + extra_query = {**extra_query, "call_id": self.__call_id} if is_async_azure_client(self.__client): - url, auth_headers = await self.__client._configure_realtime(self.__model, extra_query) + model = self.__model + if not model: + raise OpenAIError("`model` is required for Azure Realtime API") + else: + url, auth_headers = await self.__client._configure_realtime(model, extra_query) else: url = self._prepare_url().copy_with( params={ **self.__client.base_url.params, - "model": self.__model, + **({"model": self.__model} if self.__model is not omit else {}), **extra_query, }, ) @@ -470,12 +516,14 @@ def __init__( self, *, client: OpenAI, - model: str, + call_id: str | Omit = omit, + model: str | Omit = omit, extra_query: Query, extra_headers: Headers, websocket_connection_options: WebsocketConnectionOptions, ) -> None: self.__client = client + self.__call_id = call_id self.__model = model self.__connection: RealtimeConnection | None = None self.__extra_query = extra_query @@ -503,13 +551,19 @@ def __enter__(self) -> RealtimeConnection: extra_query = self.__extra_query self.__client._refresh_api_key() auth_headers = self.__client.auth_headers + if self.__call_id is not omit: + extra_query = {**extra_query, "call_id": self.__call_id} if is_azure_client(self.__client): - url, auth_headers = self.__client._configure_realtime(self.__model, extra_query) + model = self.__model + if not model: + raise OpenAIError("`model` is required for Azure Realtime API") + else: + url, auth_headers = self.__client._configure_realtime(model, extra_query) else: url = self._prepare_url().copy_with( params={ **self.__client.base_url.params, - "model": self.__model, + **({"model": self.__model} if self.__model is not omit else {}), **extra_query, }, ) diff --git a/src/openai/types/realtime/__init__.py b/src/openai/types/realtime/__init__.py index 2d947c8a2f..83e81a034a 100644 --- a/src/openai/types/realtime/__init__.py +++ b/src/openai/types/realtime/__init__.py @@ -3,8 +3,12 @@ from __future__ import annotations from .realtime_error import RealtimeError as RealtimeError +from .call_refer_params import CallReferParams as CallReferParams from .conversation_item import ConversationItem as ConversationItem from .realtime_response import RealtimeResponse as RealtimeResponse +from .call_accept_params import CallAcceptParams as CallAcceptParams +from .call_create_params import CallCreateParams as CallCreateParams +from .call_reject_params import CallRejectParams as CallRejectParams from .audio_transcription import AudioTranscription as AudioTranscription from .log_prob_properties import LogProbProperties as LogProbProperties from .realtime_truncation import RealtimeTruncation as RealtimeTruncation diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py new file mode 100644 index 0000000000..1780572e89 --- /dev/null +++ b/src/openai/types/realtime/call_accept_params.py @@ -0,0 +1,107 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .realtime_truncation_param import RealtimeTruncationParam +from .realtime_audio_config_param import RealtimeAudioConfigParam +from .realtime_tools_config_param import RealtimeToolsConfigParam +from .realtime_tracing_config_param import RealtimeTracingConfigParam +from ..responses.response_prompt_param import ResponsePromptParam +from .realtime_tool_choice_config_param import RealtimeToolChoiceConfigParam + +__all__ = ["CallAcceptParams"] + + +class CallAcceptParams(TypedDict, total=False): + type: Required[Literal["realtime"]] + """The type of session to create. Always `realtime` for the Realtime API.""" + + audio: RealtimeAudioConfigParam + """Configuration for input and output audio.""" + + include: List[Literal["item.input_audio_transcription.logprobs"]] + """Additional fields to include in server outputs. + + `item.input_audio_transcription.logprobs`: Include logprobs for input audio + transcription. + """ + + instructions: str + """The default system instructions (i.e. + + system message) prepended to model calls. This field allows the client to guide + the model on desired responses. The model can be instructed on response content + and format, (e.g. "be extremely succinct", "act friendly", "here are examples of + good responses") and on audio behavior (e.g. "talk quickly", "inject emotion + into your voice", "laugh frequently"). The instructions are not guaranteed to be + followed by the model, but they provide guidance to the model on the desired + behavior. + + Note that the server sets default instructions which will be used if this field + is not set and are visible in the `session.created` event at the start of the + session. + """ + + max_output_tokens: Union[int, Literal["inf"]] + """ + Maximum number of output tokens for a single assistant response, inclusive of + tool calls. Provide an integer between 1 and 4096 to limit output tokens, or + `inf` for the maximum available tokens for a given model. Defaults to `inf`. + """ + + model: Union[ + str, + Literal[ + "gpt-realtime", + "gpt-realtime-2025-08-28", + "gpt-4o-realtime-preview", + "gpt-4o-realtime-preview-2024-10-01", + "gpt-4o-realtime-preview-2024-12-17", + "gpt-4o-realtime-preview-2025-06-03", + "gpt-4o-mini-realtime-preview", + "gpt-4o-mini-realtime-preview-2024-12-17", + ], + ] + """The Realtime model used for this session.""" + + output_modalities: List[Literal["text", "audio"]] + """The set of modalities the model can respond with. + + It defaults to `["audio"]`, indicating that the model will respond with audio + plus a transcript. `["text"]` can be used to make the model respond with text + only. It is not possible to request both `text` and `audio` at the same time. + """ + + prompt: Optional[ResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + tool_choice: RealtimeToolChoiceConfigParam + """How the model chooses tools. + + Provide one of the string modes or force a specific function/MCP tool. + """ + + tools: RealtimeToolsConfigParam + """Tools available to the model.""" + + tracing: Optional[RealtimeTracingConfigParam] + """ + Realtime API can write session traces to the + [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once + tracing is enabled for a session, the configuration cannot be modified. + + `auto` will create a trace for the session with default values for the workflow + name, group id, and metadata. + """ + + truncation: RealtimeTruncationParam + """ + Controls how the realtime conversation is truncated prior to model inference. + The default is `auto`. + """ diff --git a/src/openai/types/realtime/call_create_params.py b/src/openai/types/realtime/call_create_params.py new file mode 100644 index 0000000000..a378092a66 --- /dev/null +++ b/src/openai/types/realtime/call_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .realtime_session_create_request_param import RealtimeSessionCreateRequestParam + +__all__ = ["CallCreateParams"] + + +class CallCreateParams(TypedDict, total=False): + sdp: Required[str] + """WebRTC Session Description Protocol (SDP) offer generated by the caller.""" + + session: RealtimeSessionCreateRequestParam + """Realtime session object configuration.""" diff --git a/src/openai/types/realtime/call_refer_params.py b/src/openai/types/realtime/call_refer_params.py new file mode 100644 index 0000000000..3d8623855b --- /dev/null +++ b/src/openai/types/realtime/call_refer_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CallReferParams"] + + +class CallReferParams(TypedDict, total=False): + target_uri: Required[str] + """URI that should appear in the SIP Refer-To header. + + Supports values like `tel:+14155550123` or `sip:agent@example.com`. + """ diff --git a/src/openai/types/realtime/call_reject_params.py b/src/openai/types/realtime/call_reject_params.py new file mode 100644 index 0000000000..f12222cded --- /dev/null +++ b/src/openai/types/realtime/call_reject_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CallRejectParams"] + + +class CallRejectParams(TypedDict, total=False): + status_code: int + """SIP response code to send back to the caller. + + Defaults to `603` (Decline) when omitted. + """ diff --git a/src/openai/types/realtime/realtime_connect_params.py b/src/openai/types/realtime/realtime_connect_params.py index 76474f3de4..950f36212f 100644 --- a/src/openai/types/realtime/realtime_connect_params.py +++ b/src/openai/types/realtime/realtime_connect_params.py @@ -2,10 +2,12 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import TypedDict __all__ = ["RealtimeConnectParams"] class RealtimeConnectParams(TypedDict, total=False): - model: Required[str] + call_id: str + + model: str diff --git a/tests/api_resources/realtime/test_calls.py b/tests/api_resources/realtime/test_calls.py new file mode 100644 index 0000000000..5495a58a4e --- /dev/null +++ b/tests/api_resources/realtime/test_calls.py @@ -0,0 +1,692 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import httpx +import pytest +from respx import MockRouter + +import openai._legacy_response as _legacy_response +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type + +# pyright: reportDeprecated=false + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCalls: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + call = client.realtime.calls.create( + sdp="sdp", + ) + assert isinstance(call, _legacy_response.HttpxBinaryResponseContent) + assert call.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + call = client.realtime.calls.create( + sdp="sdp", + session={ + "type": "realtime", + "audio": { + "input": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "noise_reduction": {"type": "near_field"}, + "transcription": { + "language": "language", + "model": "whisper-1", + "prompt": "prompt", + }, + "turn_detection": { + "type": "server_vad", + "create_response": True, + "idle_timeout_ms": 5000, + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + }, + }, + "output": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "speed": 0.25, + "voice": "ash", + }, + }, + "include": ["item.input_audio_transcription.logprobs"], + "instructions": "instructions", + "max_output_tokens": 0, + "model": "string", + "output_modalities": ["text"], + "prompt": { + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + "tool_choice": "none", + "tools": [ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + "tracing": "auto", + "truncation": "auto", + }, + ) + assert isinstance(call, _legacy_response.HttpxBinaryResponseContent) + assert call.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.realtime.calls.with_raw_response.create( + sdp="sdp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, call, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + with client.realtime.calls.with_streaming_response.create( + sdp="sdp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = response.parse() + assert_matches_type(bytes, call, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_accept(self, client: OpenAI) -> None: + call = client.realtime.calls.accept( + call_id="call_id", + type="realtime", + ) + assert call is None + + @parametrize + def test_method_accept_with_all_params(self, client: OpenAI) -> None: + call = client.realtime.calls.accept( + call_id="call_id", + type="realtime", + audio={ + "input": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "noise_reduction": {"type": "near_field"}, + "transcription": { + "language": "language", + "model": "whisper-1", + "prompt": "prompt", + }, + "turn_detection": { + "type": "server_vad", + "create_response": True, + "idle_timeout_ms": 5000, + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + }, + }, + "output": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "speed": 0.25, + "voice": "ash", + }, + }, + include=["item.input_audio_transcription.logprobs"], + instructions="instructions", + max_output_tokens=0, + model="string", + output_modalities=["text"], + prompt={ + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + tool_choice="none", + tools=[ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + tracing="auto", + truncation="auto", + ) + assert call is None + + @parametrize + def test_raw_response_accept(self, client: OpenAI) -> None: + response = client.realtime.calls.with_raw_response.accept( + call_id="call_id", + type="realtime", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + def test_streaming_response_accept(self, client: OpenAI) -> None: + with client.realtime.calls.with_streaming_response.accept( + call_id="call_id", + type="realtime", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_accept(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + client.realtime.calls.with_raw_response.accept( + call_id="", + type="realtime", + ) + + @parametrize + def test_method_hangup(self, client: OpenAI) -> None: + call = client.realtime.calls.hangup( + "call_id", + ) + assert call is None + + @parametrize + def test_raw_response_hangup(self, client: OpenAI) -> None: + response = client.realtime.calls.with_raw_response.hangup( + "call_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + def test_streaming_response_hangup(self, client: OpenAI) -> None: + with client.realtime.calls.with_streaming_response.hangup( + "call_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_hangup(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + client.realtime.calls.with_raw_response.hangup( + "", + ) + + @parametrize + def test_method_refer(self, client: OpenAI) -> None: + call = client.realtime.calls.refer( + call_id="call_id", + target_uri="tel:+14155550123", + ) + assert call is None + + @parametrize + def test_raw_response_refer(self, client: OpenAI) -> None: + response = client.realtime.calls.with_raw_response.refer( + call_id="call_id", + target_uri="tel:+14155550123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + def test_streaming_response_refer(self, client: OpenAI) -> None: + with client.realtime.calls.with_streaming_response.refer( + call_id="call_id", + target_uri="tel:+14155550123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_refer(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + client.realtime.calls.with_raw_response.refer( + call_id="", + target_uri="tel:+14155550123", + ) + + @parametrize + def test_method_reject(self, client: OpenAI) -> None: + call = client.realtime.calls.reject( + call_id="call_id", + ) + assert call is None + + @parametrize + def test_method_reject_with_all_params(self, client: OpenAI) -> None: + call = client.realtime.calls.reject( + call_id="call_id", + status_code=486, + ) + assert call is None + + @parametrize + def test_raw_response_reject(self, client: OpenAI) -> None: + response = client.realtime.calls.with_raw_response.reject( + call_id="call_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + def test_streaming_response_reject(self, client: OpenAI) -> None: + with client.realtime.calls.with_streaming_response.reject( + call_id="call_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_reject(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + client.realtime.calls.with_raw_response.reject( + call_id="", + ) + + +class TestAsyncCalls: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + call = await async_client.realtime.calls.create( + sdp="sdp", + ) + assert isinstance(call, _legacy_response.HttpxBinaryResponseContent) + assert call.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + call = await async_client.realtime.calls.create( + sdp="sdp", + session={ + "type": "realtime", + "audio": { + "input": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "noise_reduction": {"type": "near_field"}, + "transcription": { + "language": "language", + "model": "whisper-1", + "prompt": "prompt", + }, + "turn_detection": { + "type": "server_vad", + "create_response": True, + "idle_timeout_ms": 5000, + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + }, + }, + "output": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "speed": 0.25, + "voice": "ash", + }, + }, + "include": ["item.input_audio_transcription.logprobs"], + "instructions": "instructions", + "max_output_tokens": 0, + "model": "string", + "output_modalities": ["text"], + "prompt": { + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + "tool_choice": "none", + "tools": [ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + "tracing": "auto", + "truncation": "auto", + }, + ) + assert isinstance(call, _legacy_response.HttpxBinaryResponseContent) + assert call.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.realtime.calls.with_raw_response.create( + sdp="sdp", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, call, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.post("/realtime/calls").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + async with async_client.realtime.calls.with_streaming_response.create( + sdp="sdp", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = await response.parse() + assert_matches_type(bytes, call, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_accept(self, async_client: AsyncOpenAI) -> None: + call = await async_client.realtime.calls.accept( + call_id="call_id", + type="realtime", + ) + assert call is None + + @parametrize + async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> None: + call = await async_client.realtime.calls.accept( + call_id="call_id", + type="realtime", + audio={ + "input": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "noise_reduction": {"type": "near_field"}, + "transcription": { + "language": "language", + "model": "whisper-1", + "prompt": "prompt", + }, + "turn_detection": { + "type": "server_vad", + "create_response": True, + "idle_timeout_ms": 5000, + "interrupt_response": True, + "prefix_padding_ms": 0, + "silence_duration_ms": 0, + "threshold": 0, + }, + }, + "output": { + "format": { + "rate": 24000, + "type": "audio/pcm", + }, + "speed": 0.25, + "voice": "ash", + }, + }, + include=["item.input_audio_transcription.logprobs"], + instructions="instructions", + max_output_tokens=0, + model="string", + output_modalities=["text"], + prompt={ + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + tool_choice="none", + tools=[ + { + "description": "description", + "name": "name", + "parameters": {}, + "type": "function", + } + ], + tracing="auto", + truncation="auto", + ) + assert call is None + + @parametrize + async def test_raw_response_accept(self, async_client: AsyncOpenAI) -> None: + response = await async_client.realtime.calls.with_raw_response.accept( + call_id="call_id", + type="realtime", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + async def test_streaming_response_accept(self, async_client: AsyncOpenAI) -> None: + async with async_client.realtime.calls.with_streaming_response.accept( + call_id="call_id", + type="realtime", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = await response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_accept(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + await async_client.realtime.calls.with_raw_response.accept( + call_id="", + type="realtime", + ) + + @parametrize + async def test_method_hangup(self, async_client: AsyncOpenAI) -> None: + call = await async_client.realtime.calls.hangup( + "call_id", + ) + assert call is None + + @parametrize + async def test_raw_response_hangup(self, async_client: AsyncOpenAI) -> None: + response = await async_client.realtime.calls.with_raw_response.hangup( + "call_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + async def test_streaming_response_hangup(self, async_client: AsyncOpenAI) -> None: + async with async_client.realtime.calls.with_streaming_response.hangup( + "call_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = await response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_hangup(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + await async_client.realtime.calls.with_raw_response.hangup( + "", + ) + + @parametrize + async def test_method_refer(self, async_client: AsyncOpenAI) -> None: + call = await async_client.realtime.calls.refer( + call_id="call_id", + target_uri="tel:+14155550123", + ) + assert call is None + + @parametrize + async def test_raw_response_refer(self, async_client: AsyncOpenAI) -> None: + response = await async_client.realtime.calls.with_raw_response.refer( + call_id="call_id", + target_uri="tel:+14155550123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + async def test_streaming_response_refer(self, async_client: AsyncOpenAI) -> None: + async with async_client.realtime.calls.with_streaming_response.refer( + call_id="call_id", + target_uri="tel:+14155550123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = await response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_refer(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + await async_client.realtime.calls.with_raw_response.refer( + call_id="", + target_uri="tel:+14155550123", + ) + + @parametrize + async def test_method_reject(self, async_client: AsyncOpenAI) -> None: + call = await async_client.realtime.calls.reject( + call_id="call_id", + ) + assert call is None + + @parametrize + async def test_method_reject_with_all_params(self, async_client: AsyncOpenAI) -> None: + call = await async_client.realtime.calls.reject( + call_id="call_id", + status_code=486, + ) + assert call is None + + @parametrize + async def test_raw_response_reject(self, async_client: AsyncOpenAI) -> None: + response = await async_client.realtime.calls.with_raw_response.reject( + call_id="call_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + call = response.parse() + assert call is None + + @parametrize + async def test_streaming_response_reject(self, async_client: AsyncOpenAI) -> None: + async with async_client.realtime.calls.with_streaming_response.reject( + call_id="call_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + call = await response.parse() + assert call is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_reject(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `call_id` but received ''"): + await async_client.realtime.calls.with_raw_response.reject( + call_id="", + ) From 53f7a74ca4af71fed7f61d6a73c5e597a99898ac Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 20:13:57 +0000 Subject: [PATCH 127/408] release: 2.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cf7239890c..656a2ef17d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.0.1" + ".": "2.1.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb9926c29..76d700f05e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.1.0 (2025-10-02) + +Full Changelog: [v2.0.1...v2.1.0](https://github.com/openai/openai-python/compare/v2.0.1...v2.1.0) + +### Features + +* **api:** add support for realtime calls ([7f7925b](https://github.com/openai/openai-python/commit/7f7925b4074ecbf879714698000e10fa0519d51a)) + ## 2.0.1 (2025-10-01) Full Changelog: [v2.0.0...v2.0.1](https://github.com/openai/openai-python/compare/v2.0.0...v2.0.1) diff --git a/pyproject.toml b/pyproject.toml index a83bca2dcd..d8deac4e61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.0.1" +version = "2.1.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f20794ac19..2860526ced 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.0.1" # x-release-please-version +__version__ = "2.1.0" # x-release-please-version From 9ada2c74f3f5865a2bfb19afce885cc98ad6a4b3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:49:02 +0000 Subject: [PATCH 128/408] feat(api): dev day 2025 launches DevDay 2025 launches including videos and chatkit beta --- .stats.yml | 8 +- api.md | 26 + examples/video.py | 22 + helpers.md | 1 + src/openai/__init__.py | 1 + src/openai/_client.py | 38 + src/openai/_module_client.py | 8 + src/openai/resources/__init__.py | 14 + src/openai/resources/beta/__init__.py | 14 + src/openai/resources/beta/beta.py | 38 +- src/openai/resources/beta/chatkit/__init__.py | 47 + src/openai/resources/beta/chatkit/chatkit.py | 259 ++++++ src/openai/resources/beta/chatkit/sessions.py | 301 +++++++ src/openai/resources/beta/chatkit/threads.py | 521 +++++++++++ src/openai/resources/images.py | 18 +- src/openai/resources/realtime/calls.py | 8 + src/openai/resources/realtime/realtime.py | 6 +- src/openai/resources/videos.py | 847 ++++++++++++++++++ src/openai/types/__init__.py | 10 + src/openai/types/beta/__init__.py | 5 + src/openai/types/beta/chatkit/__init__.py | 32 + src/openai/types/beta/chatkit/chat_session.py | 43 + .../chat_session_automatic_thread_titling.py | 10 + .../chat_session_chatkit_configuration.py | 19 + ...hat_session_chatkit_configuration_param.py | 59 ++ .../chat_session_expires_after_param.py | 15 + .../beta/chatkit/chat_session_file_upload.py | 18 + .../beta/chatkit/chat_session_history.py | 18 + .../beta/chatkit/chat_session_rate_limits.py | 10 + .../chatkit/chat_session_rate_limits_param.py | 12 + .../types/beta/chatkit/chat_session_status.py | 7 + .../chatkit/chat_session_workflow_param.py | 34 + .../types/beta/chatkit/chatkit_attachment.py | 25 + .../chatkit/chatkit_response_output_text.py | 62 ++ .../types/beta/chatkit/chatkit_thread.py | 56 ++ .../chatkit_thread_assistant_message_item.py | 29 + .../beta/chatkit/chatkit_thread_item_list.py | 144 +++ .../chatkit_thread_user_message_item.py | 77 ++ .../types/beta/chatkit/chatkit_widget_item.py | 27 + .../beta/chatkit/session_create_params.py | 35 + .../beta/chatkit/thread_delete_response.py | 18 + .../beta/chatkit/thread_list_items_params.py | 27 + .../types/beta/chatkit/thread_list_params.py | 33 + .../types/beta/chatkit_upload_file_params.py | 17 + .../beta/chatkit_upload_file_response.py | 12 + src/openai/types/beta/chatkit_workflow.py | 32 + src/openai/types/beta/file_part.py | 28 + src/openai/types/beta/image_part.py | 31 + src/openai/types/image_edit_params.py | 13 +- src/openai/types/image_generate_params.py | 10 +- src/openai/types/image_model.py | 2 +- .../types/realtime/call_accept_params.py | 4 + .../realtime_session_create_request.py | 4 + .../realtime_session_create_request_param.py | 4 + .../realtime_session_create_response.py | 4 + src/openai/types/responses/tool.py | 5 +- src/openai/types/responses/tool_param.py | 5 +- src/openai/types/shared/all_models.py | 2 + src/openai/types/shared/responses_model.py | 2 + .../types/shared_params/responses_model.py | 2 + src/openai/types/video.py | 50 ++ src/openai/types/video_create_error.py | 11 + src/openai/types/video_create_params.py | 29 + src/openai/types/video_delete_response.py | 18 + .../types/video_download_content_params.py | 12 + src/openai/types/video_list_params.py | 21 + src/openai/types/video_model.py | 7 + src/openai/types/video_remix_params.py | 12 + src/openai/types/video_seconds.py | 7 + src/openai/types/video_size.py | 7 + tests/api_resources/beta/chatkit/__init__.py | 1 + .../beta/chatkit/test_sessions.py | 230 +++++ .../beta/chatkit/test_threads.py | 348 +++++++ tests/api_resources/beta/test_chatkit.py | 86 ++ tests/api_resources/test_videos.py | 551 ++++++++++++ 75 files changed, 4527 insertions(+), 42 deletions(-) create mode 100644 examples/video.py create mode 100644 src/openai/resources/beta/chatkit/__init__.py create mode 100644 src/openai/resources/beta/chatkit/chatkit.py create mode 100644 src/openai/resources/beta/chatkit/sessions.py create mode 100644 src/openai/resources/beta/chatkit/threads.py create mode 100644 src/openai/resources/videos.py create mode 100644 src/openai/types/beta/chatkit/__init__.py create mode 100644 src/openai/types/beta/chatkit/chat_session.py create mode 100644 src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py create mode 100644 src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py create mode 100644 src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py create mode 100644 src/openai/types/beta/chatkit/chat_session_expires_after_param.py create mode 100644 src/openai/types/beta/chatkit/chat_session_file_upload.py create mode 100644 src/openai/types/beta/chatkit/chat_session_history.py create mode 100644 src/openai/types/beta/chatkit/chat_session_rate_limits.py create mode 100644 src/openai/types/beta/chatkit/chat_session_rate_limits_param.py create mode 100644 src/openai/types/beta/chatkit/chat_session_status.py create mode 100644 src/openai/types/beta/chatkit/chat_session_workflow_param.py create mode 100644 src/openai/types/beta/chatkit/chatkit_attachment.py create mode 100644 src/openai/types/beta/chatkit/chatkit_response_output_text.py create mode 100644 src/openai/types/beta/chatkit/chatkit_thread.py create mode 100644 src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py create mode 100644 src/openai/types/beta/chatkit/chatkit_thread_item_list.py create mode 100644 src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py create mode 100644 src/openai/types/beta/chatkit/chatkit_widget_item.py create mode 100644 src/openai/types/beta/chatkit/session_create_params.py create mode 100644 src/openai/types/beta/chatkit/thread_delete_response.py create mode 100644 src/openai/types/beta/chatkit/thread_list_items_params.py create mode 100644 src/openai/types/beta/chatkit/thread_list_params.py create mode 100644 src/openai/types/beta/chatkit_upload_file_params.py create mode 100644 src/openai/types/beta/chatkit_upload_file_response.py create mode 100644 src/openai/types/beta/chatkit_workflow.py create mode 100644 src/openai/types/beta/file_part.py create mode 100644 src/openai/types/beta/image_part.py create mode 100644 src/openai/types/video.py create mode 100644 src/openai/types/video_create_error.py create mode 100644 src/openai/types/video_create_params.py create mode 100644 src/openai/types/video_delete_response.py create mode 100644 src/openai/types/video_download_content_params.py create mode 100644 src/openai/types/video_list_params.py create mode 100644 src/openai/types/video_model.py create mode 100644 src/openai/types/video_remix_params.py create mode 100644 src/openai/types/video_seconds.py create mode 100644 src/openai/types/video_size.py create mode 100644 tests/api_resources/beta/chatkit/__init__.py create mode 100644 tests/api_resources/beta/chatkit/test_sessions.py create mode 100644 tests/api_resources/beta/chatkit/test_threads.py create mode 100644 tests/api_resources/beta/test_chatkit.py create mode 100644 tests/api_resources/test_videos.py diff --git a/.stats.yml b/.stats.yml index d974760a99..2dc7422e7e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 123 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fadefdc7c7e30df47c09df323669b242ff90ee08e51f304175ace5274e0aab49.yml -openapi_spec_hash: 6d20f639d9ff8a097a34962da6218231 -config_hash: 902654e60f5d659f2bfcfd903e17c46d +configured_endpoints: 136 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d64cf80d2ebddf175c5578f68226a3d5bbd3f7fd8d62ccac2205f3fc05a355ee.yml +openapi_spec_hash: d51e0d60d0c536f210b597a211bc5af0 +config_hash: e7c42016df9c6bd7bd6ff15101b9bc9b diff --git a/api.md b/api.md index d5331803ce..1c170ccdd8 100644 --- a/api.md +++ b/api.md @@ -1139,3 +1139,29 @@ Methods: Methods: - client.containers.files.content.retrieve(file_id, \*, container_id) -> HttpxBinaryResponseContent + +# Videos + +Types: + +```python +from openai.types import ( + Video, + VideoCreateError, + VideoModel, + VideoSeconds, + VideoSize, + VideoDeleteResponse, +) +``` + +Methods: + +- client.videos.create(\*\*params) -> Video +- client.videos.retrieve(video_id) -> Video +- client.videos.list(\*\*params) -> SyncConversationCursorPage[Video] +- client.videos.delete(video_id) -> VideoDeleteResponse +- client.videos.download_content(video_id, \*\*params) -> HttpxBinaryResponseContent +- client.videos.remix(video_id, \*\*params) -> Video +- client.videos.create_and_poll(\*args) -> Video + diff --git a/examples/video.py b/examples/video.py new file mode 100644 index 0000000000..ee89e64697 --- /dev/null +++ b/examples/video.py @@ -0,0 +1,22 @@ +#!/usr/bin/env -S poetry run python + +import asyncio + +from openai import AsyncOpenAI + +client = AsyncOpenAI() + + +async def main() -> None: + video = await client.videos.create_and_poll( + model="sora-2", + prompt="A video of the words 'Thank you' in sparkling letters", + ) + + if video.status == "completed": + print("Video successfully completed: ", video) + else: + print("Video creation failed. Status: ", video.status) + + +asyncio.run(main()) diff --git a/helpers.md b/helpers.md index 21ad8ac2fb..89ff4498cf 100644 --- a/helpers.md +++ b/helpers.md @@ -514,4 +514,5 @@ client.beta.vector_stores.files.upload_and_poll(...) client.beta.vector_stores.files.create_and_poll(...) client.beta.vector_stores.file_batches.create_and_poll(...) client.beta.vector_stores.file_batches.upload_and_poll(...) +client.videos.create_and_poll(...) ``` diff --git a/src/openai/__init__.py b/src/openai/__init__.py index bd01da628d..e7411b3886 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -379,6 +379,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] files as files, images as images, models as models, + videos as videos, batches as batches, uploads as uploads, realtime as realtime, diff --git a/src/openai/_client.py b/src/openai/_client.py index 1485029ddd..a3b01b2ce6 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -44,6 +44,7 @@ files, images, models, + videos, batches, uploads, realtime, @@ -59,6 +60,7 @@ from .resources.files import Files, AsyncFiles from .resources.images import Images, AsyncImages from .resources.models import Models, AsyncModels + from .resources.videos import Videos, AsyncVideos from .resources.batches import Batches, AsyncBatches from .resources.webhooks import Webhooks, AsyncWebhooks from .resources.beta.beta import Beta, AsyncBeta @@ -288,6 +290,12 @@ def containers(self) -> Containers: return Containers(self) + @cached_property + def videos(self) -> Videos: + from .resources.videos import Videos + + return Videos(self) + @cached_property def with_raw_response(self) -> OpenAIWithRawResponse: return OpenAIWithRawResponse(self) @@ -633,6 +641,12 @@ def containers(self) -> AsyncContainers: return AsyncContainers(self) + @cached_property + def videos(self) -> AsyncVideos: + from .resources.videos import AsyncVideos + + return AsyncVideos(self) + @cached_property def with_raw_response(self) -> AsyncOpenAIWithRawResponse: return AsyncOpenAIWithRawResponse(self) @@ -883,6 +897,12 @@ def containers(self) -> containers.ContainersWithRawResponse: return ContainersWithRawResponse(self._client.containers) + @cached_property + def videos(self) -> videos.VideosWithRawResponse: + from .resources.videos import VideosWithRawResponse + + return VideosWithRawResponse(self._client.videos) + class AsyncOpenAIWithRawResponse: _client: AsyncOpenAI @@ -998,6 +1018,12 @@ def containers(self) -> containers.AsyncContainersWithRawResponse: return AsyncContainersWithRawResponse(self._client.containers) + @cached_property + def videos(self) -> videos.AsyncVideosWithRawResponse: + from .resources.videos import AsyncVideosWithRawResponse + + return AsyncVideosWithRawResponse(self._client.videos) + class OpenAIWithStreamedResponse: _client: OpenAI @@ -1113,6 +1139,12 @@ def containers(self) -> containers.ContainersWithStreamingResponse: return ContainersWithStreamingResponse(self._client.containers) + @cached_property + def videos(self) -> videos.VideosWithStreamingResponse: + from .resources.videos import VideosWithStreamingResponse + + return VideosWithStreamingResponse(self._client.videos) + class AsyncOpenAIWithStreamedResponse: _client: AsyncOpenAI @@ -1228,6 +1260,12 @@ def containers(self) -> containers.AsyncContainersWithStreamingResponse: return AsyncContainersWithStreamingResponse(self._client.containers) + @cached_property + def videos(self) -> videos.AsyncVideosWithStreamingResponse: + from .resources.videos import AsyncVideosWithStreamingResponse + + return AsyncVideosWithStreamingResponse(self._client.videos) + Client = OpenAI diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index 4ecc28420a..d0d721887b 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -9,6 +9,7 @@ from .resources.files import Files from .resources.images import Images from .resources.models import Models + from .resources.videos import Videos from .resources.batches import Batches from .resources.webhooks import Webhooks from .resources.beta.beta import Beta @@ -72,6 +73,12 @@ def __load__(self) -> Models: return _load_client().models +class VideosProxy(LazyProxy["Videos"]): + @override + def __load__(self) -> Videos: + return _load_client().videos + + class BatchesProxy(LazyProxy["Batches"]): @override def __load__(self) -> Batches: @@ -151,6 +158,7 @@ def __load__(self) -> Conversations: evals: Evals = EvalsProxy().__as_proxied__() images: Images = ImagesProxy().__as_proxied__() models: Models = ModelsProxy().__as_proxied__() +videos: Videos = VideosProxy().__as_proxied__() batches: Batches = BatchesProxy().__as_proxied__() uploads: Uploads = UploadsProxy().__as_proxied__() webhooks: Webhooks = WebhooksProxy().__as_proxied__() diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index 82c9f037d9..b793fbc7b0 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -56,6 +56,14 @@ ModelsWithStreamingResponse, AsyncModelsWithStreamingResponse, ) +from .videos import ( + Videos, + AsyncVideos, + VideosWithRawResponse, + AsyncVideosWithRawResponse, + VideosWithStreamingResponse, + AsyncVideosWithStreamingResponse, +) from .batches import ( Batches, AsyncBatches, @@ -212,4 +220,10 @@ "AsyncContainersWithRawResponse", "ContainersWithStreamingResponse", "AsyncContainersWithStreamingResponse", + "Videos", + "AsyncVideos", + "VideosWithRawResponse", + "AsyncVideosWithRawResponse", + "VideosWithStreamingResponse", + "AsyncVideosWithStreamingResponse", ] diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 87fea25267..6d6f538670 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -8,6 +8,14 @@ BetaWithStreamingResponse, AsyncBetaWithStreamingResponse, ) +from .chatkit import ( + ChatKit, + AsyncChatKit, + ChatKitWithRawResponse, + AsyncChatKitWithRawResponse, + ChatKitWithStreamingResponse, + AsyncChatKitWithStreamingResponse, +) from .threads import ( Threads, AsyncThreads, @@ -26,6 +34,12 @@ ) __all__ = [ + "ChatKit", + "AsyncChatKit", + "ChatKitWithRawResponse", + "AsyncChatKitWithRawResponse", + "ChatKitWithStreamingResponse", + "AsyncChatKitWithStreamingResponse", "Assistants", "AsyncAssistants", "AssistantsWithRawResponse", diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 9084c477e9..81a6e7aa93 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -12,6 +12,14 @@ AsyncAssistantsWithStreamingResponse, ) from ..._resource import SyncAPIResource, AsyncAPIResource +from .chatkit.chatkit import ( + ChatKit, + AsyncChatKit, + ChatKitWithRawResponse, + AsyncChatKitWithRawResponse, + ChatKitWithStreamingResponse, + AsyncChatKitWithStreamingResponse, +) from .threads.threads import ( Threads, AsyncThreads, @@ -31,14 +39,7 @@ class Beta(SyncAPIResource): @cached_property - def chat(self) -> Chat: - return Chat(self._client) - - @cached_property - def realtime(self) -> Realtime: - return Realtime(self._client) - @cached_property def assistants(self) -> Assistants: return Assistants(self._client) @@ -68,14 +69,7 @@ def with_streaming_response(self) -> BetaWithStreamingResponse: class AsyncBeta(AsyncAPIResource): @cached_property - def chat(self) -> AsyncChat: - return AsyncChat(self._client) - @cached_property - def realtime(self) -> AsyncRealtime: - return AsyncRealtime(self._client) - - @cached_property def assistants(self) -> AsyncAssistants: return AsyncAssistants(self._client) @@ -107,6 +101,10 @@ class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def chatkit(self) -> ChatKitWithRawResponse: + return ChatKitWithRawResponse(self._beta.chatkit) + @cached_property def assistants(self) -> AssistantsWithRawResponse: return AssistantsWithRawResponse(self._beta.assistants) @@ -120,6 +118,10 @@ class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def chatkit(self) -> AsyncChatKitWithRawResponse: + return AsyncChatKitWithRawResponse(self._beta.chatkit) + @cached_property def assistants(self) -> AsyncAssistantsWithRawResponse: return AsyncAssistantsWithRawResponse(self._beta.assistants) @@ -133,6 +135,10 @@ class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def chatkit(self) -> ChatKitWithStreamingResponse: + return ChatKitWithStreamingResponse(self._beta.chatkit) + @cached_property def assistants(self) -> AssistantsWithStreamingResponse: return AssistantsWithStreamingResponse(self._beta.assistants) @@ -146,6 +152,10 @@ class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def chatkit(self) -> AsyncChatKitWithStreamingResponse: + return AsyncChatKitWithStreamingResponse(self._beta.chatkit) + @cached_property def assistants(self) -> AsyncAssistantsWithStreamingResponse: return AsyncAssistantsWithStreamingResponse(self._beta.assistants) diff --git a/src/openai/resources/beta/chatkit/__init__.py b/src/openai/resources/beta/chatkit/__init__.py new file mode 100644 index 0000000000..05f24d6238 --- /dev/null +++ b/src/openai/resources/beta/chatkit/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .chatkit import ( + ChatKit, + AsyncChatKit, + ChatKitWithRawResponse, + AsyncChatKitWithRawResponse, + ChatKitWithStreamingResponse, + AsyncChatKitWithStreamingResponse, +) +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) + +__all__ = [ + "Sessions", + "AsyncSessions", + "SessionsWithRawResponse", + "AsyncSessionsWithRawResponse", + "SessionsWithStreamingResponse", + "AsyncSessionsWithStreamingResponse", + "Threads", + "AsyncThreads", + "ThreadsWithRawResponse", + "AsyncThreadsWithRawResponse", + "ThreadsWithStreamingResponse", + "AsyncThreadsWithStreamingResponse", + "ChatKit", + "AsyncChatKit", + "ChatKitWithRawResponse", + "AsyncChatKitWithRawResponse", + "ChatKitWithStreamingResponse", + "AsyncChatKitWithStreamingResponse", +] diff --git a/src/openai/resources/beta/chatkit/chatkit.py b/src/openai/resources/beta/chatkit/chatkit.py new file mode 100644 index 0000000000..2d090cf396 --- /dev/null +++ b/src/openai/resources/beta/chatkit/chatkit.py @@ -0,0 +1,259 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, Mapping, cast + +import httpx + +from .... import _legacy_response +from .threads import ( + Threads, + AsyncThreads, + ThreadsWithRawResponse, + AsyncThreadsWithRawResponse, + ThreadsWithStreamingResponse, + AsyncThreadsWithStreamingResponse, +) +from .sessions import ( + Sessions, + AsyncSessions, + SessionsWithRawResponse, + AsyncSessionsWithRawResponse, + SessionsWithStreamingResponse, + AsyncSessionsWithStreamingResponse, +) +from ...._types import Body, Query, Headers, NotGiven, FileTypes, not_given +from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....types.beta import chatkit_upload_file_params +from ...._base_client import make_request_options +from ....types.beta.chatkit_upload_file_response import ChatKitUploadFileResponse + +__all__ = ["ChatKit", "AsyncChatKit"] + + +class ChatKit(SyncAPIResource): + @cached_property + def sessions(self) -> Sessions: + return Sessions(self._client) + + @cached_property + def threads(self) -> Threads: + return Threads(self._client) + + @cached_property + def with_raw_response(self) -> ChatKitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ChatKitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChatKitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ChatKitWithStreamingResponse(self) + + def upload_file( + self, + *, + file: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatKitUploadFileResponse: + """ + Upload a ChatKit file + + Args: + file: Binary file contents to store with the ChatKit session. Supports PDFs and PNG, + JPG, JPEG, GIF, or WEBP images. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + body = deepcopy_minimal({"file": file}) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers["Content-Type"] = "multipart/form-data" + return cast( + ChatKitUploadFileResponse, + self._post( + "/chatkit/files", + body=maybe_transform(body, chatkit_upload_file_params.ChatKitUploadFileParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, ChatKitUploadFileResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + +class AsyncChatKit(AsyncAPIResource): + @cached_property + def sessions(self) -> AsyncSessions: + return AsyncSessions(self._client) + + @cached_property + def threads(self) -> AsyncThreads: + return AsyncThreads(self._client) + + @cached_property + def with_raw_response(self) -> AsyncChatKitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncChatKitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChatKitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncChatKitWithStreamingResponse(self) + + async def upload_file( + self, + *, + file: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatKitUploadFileResponse: + """ + Upload a ChatKit file + + Args: + file: Binary file contents to store with the ChatKit session. Supports PDFs and PNG, + JPG, JPEG, GIF, or WEBP images. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + body = deepcopy_minimal({"file": file}) + files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers["Content-Type"] = "multipart/form-data" + return cast( + ChatKitUploadFileResponse, + await self._post( + "/chatkit/files", + body=await async_maybe_transform(body, chatkit_upload_file_params.ChatKitUploadFileParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=cast( + Any, ChatKitUploadFileResponse + ), # Union types cannot be passed in as arguments in the type system + ), + ) + + +class ChatKitWithRawResponse: + def __init__(self, chatkit: ChatKit) -> None: + self._chatkit = chatkit + + self.upload_file = _legacy_response.to_raw_response_wrapper( + chatkit.upload_file, + ) + + @cached_property + def sessions(self) -> SessionsWithRawResponse: + return SessionsWithRawResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> ThreadsWithRawResponse: + return ThreadsWithRawResponse(self._chatkit.threads) + + +class AsyncChatKitWithRawResponse: + def __init__(self, chatkit: AsyncChatKit) -> None: + self._chatkit = chatkit + + self.upload_file = _legacy_response.async_to_raw_response_wrapper( + chatkit.upload_file, + ) + + @cached_property + def sessions(self) -> AsyncSessionsWithRawResponse: + return AsyncSessionsWithRawResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> AsyncThreadsWithRawResponse: + return AsyncThreadsWithRawResponse(self._chatkit.threads) + + +class ChatKitWithStreamingResponse: + def __init__(self, chatkit: ChatKit) -> None: + self._chatkit = chatkit + + self.upload_file = to_streamed_response_wrapper( + chatkit.upload_file, + ) + + @cached_property + def sessions(self) -> SessionsWithStreamingResponse: + return SessionsWithStreamingResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> ThreadsWithStreamingResponse: + return ThreadsWithStreamingResponse(self._chatkit.threads) + + +class AsyncChatKitWithStreamingResponse: + def __init__(self, chatkit: AsyncChatKit) -> None: + self._chatkit = chatkit + + self.upload_file = async_to_streamed_response_wrapper( + chatkit.upload_file, + ) + + @cached_property + def sessions(self) -> AsyncSessionsWithStreamingResponse: + return AsyncSessionsWithStreamingResponse(self._chatkit.sessions) + + @cached_property + def threads(self) -> AsyncThreadsWithStreamingResponse: + return AsyncThreadsWithStreamingResponse(self._chatkit.threads) diff --git a/src/openai/resources/beta/chatkit/sessions.py b/src/openai/resources/beta/chatkit/sessions.py new file mode 100644 index 0000000000..a814f1058e --- /dev/null +++ b/src/openai/resources/beta/chatkit/sessions.py @@ -0,0 +1,301 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.chatkit import ( + ChatSessionWorkflowParam, + ChatSessionRateLimitsParam, + ChatSessionExpiresAfterParam, + ChatSessionChatKitConfigurationParam, + session_create_params, +) +from ....types.beta.chatkit.chat_session import ChatSession +from ....types.beta.chatkit.chat_session_workflow_param import ChatSessionWorkflowParam +from ....types.beta.chatkit.chat_session_rate_limits_param import ChatSessionRateLimitsParam +from ....types.beta.chatkit.chat_session_expires_after_param import ChatSessionExpiresAfterParam +from ....types.beta.chatkit.chat_session_chatkit_configuration_param import ChatSessionChatKitConfigurationParam + +__all__ = ["Sessions", "AsyncSessions"] + + +class Sessions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SessionsWithStreamingResponse(self) + + def create( + self, + *, + user: str, + workflow: ChatSessionWorkflowParam, + chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, + expires_after: ChatSessionExpiresAfterParam | Omit = omit, + rate_limits: ChatSessionRateLimitsParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Create a ChatKit session + + Args: + user: A free-form string that identifies your end user; ensures this Session can + access other objects that have the same `user` scope. + + workflow: Workflow that powers the session. + + chatkit_configuration: Optional overrides for ChatKit runtime configuration features + + expires_after: Optional override for session expiration timing in seconds from creation. + Defaults to 10 minutes. + + rate_limits: Optional override for per-minute request limits. When omitted, defaults to 10. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._post( + "/chatkit/sessions", + body=maybe_transform( + { + "user": user, + "workflow": workflow, + "chatkit_configuration": chatkit_configuration, + "expires_after": expires_after, + "rate_limits": rate_limits, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatSession, + ) + + def cancel( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Cancel a ChatKit session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._post( + f"/chatkit/sessions/{session_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatSession, + ) + + +class AsyncSessions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSessionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSessionsWithStreamingResponse(self) + + async def create( + self, + *, + user: str, + workflow: ChatSessionWorkflowParam, + chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, + expires_after: ChatSessionExpiresAfterParam | Omit = omit, + rate_limits: ChatSessionRateLimitsParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Create a ChatKit session + + Args: + user: A free-form string that identifies your end user; ensures this Session can + access other objects that have the same `user` scope. + + workflow: Workflow that powers the session. + + chatkit_configuration: Optional overrides for ChatKit runtime configuration features + + expires_after: Optional override for session expiration timing in seconds from creation. + Defaults to 10 minutes. + + rate_limits: Optional override for per-minute request limits. When omitted, defaults to 10. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._post( + "/chatkit/sessions", + body=await async_maybe_transform( + { + "user": user, + "workflow": workflow, + "chatkit_configuration": chatkit_configuration, + "expires_after": expires_after, + "rate_limits": rate_limits, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatSession, + ) + + async def cancel( + self, + session_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatSession: + """ + Cancel a ChatKit session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not session_id: + raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._post( + f"/chatkit/sessions/{session_id}/cancel", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatSession, + ) + + +class SessionsWithRawResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.to_raw_response_wrapper( + sessions.create, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + sessions.cancel, + ) + + +class AsyncSessionsWithRawResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = _legacy_response.async_to_raw_response_wrapper( + sessions.create, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + sessions.cancel, + ) + + +class SessionsWithStreamingResponse: + def __init__(self, sessions: Sessions) -> None: + self._sessions = sessions + + self.create = to_streamed_response_wrapper( + sessions.create, + ) + self.cancel = to_streamed_response_wrapper( + sessions.cancel, + ) + + +class AsyncSessionsWithStreamingResponse: + def __init__(self, sessions: AsyncSessions) -> None: + self._sessions = sessions + + self.create = async_to_streamed_response_wrapper( + sessions.create, + ) + self.cancel = async_to_streamed_response_wrapper( + sessions.cancel, + ) diff --git a/src/openai/resources/beta/chatkit/threads.py b/src/openai/resources/beta/chatkit/threads.py new file mode 100644 index 0000000000..37cd57295a --- /dev/null +++ b/src/openai/resources/beta/chatkit/threads.py @@ -0,0 +1,521 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, cast +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.beta.chatkit import thread_list_params, thread_list_items_params +from ....types.beta.chatkit.chatkit_thread import ChatKitThread +from ....types.beta.chatkit.thread_delete_response import ThreadDeleteResponse +from ....types.beta.chatkit.chatkit_thread_item_list import Data + +__all__ = ["Threads", "AsyncThreads"] + + +class Threads(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ThreadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ThreadsWithStreamingResponse(self) + + def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatKitThread: + """ + Retrieve a ChatKit thread + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get( + f"/chatkit/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatKitThread, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ChatKitThread]: + """ + List ChatKit threads + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + user: Filter threads that belong to this user identifier. Defaults to null to return + all users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + "/chatkit/threads", + page=SyncConversationCursorPage[ChatKitThread], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + "user": user, + }, + thread_list_params.ThreadListParams, + ), + ), + model=ChatKitThread, + ) + + def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ThreadDeleteResponse: + """ + Delete a ChatKit thread + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._delete( + f"/chatkit/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadDeleteResponse, + ) + + def list_items( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Data]: + """ + List ChatKit thread items + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + f"/chatkit/threads/{thread_id}/items", + page=SyncConversationCursorPage[Data], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + thread_list_items_params.ThreadListItemsParams, + ), + ), + model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncThreads(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncThreadsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncThreadsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncThreadsWithStreamingResponse(self) + + async def retrieve( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ChatKitThread: + """ + Retrieve a ChatKit thread + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._get( + f"/chatkit/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChatKitThread, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + user: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ChatKitThread, AsyncConversationCursorPage[ChatKitThread]]: + """ + List ChatKit threads + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + user: Filter threads that belong to this user identifier. Defaults to null to return + all users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + "/chatkit/threads", + page=AsyncConversationCursorPage[ChatKitThread], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + "user": user, + }, + thread_list_params.ThreadListParams, + ), + ), + model=ChatKitThread, + ) + + async def delete( + self, + thread_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ThreadDeleteResponse: + """ + Delete a ChatKit thread + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return await self._delete( + f"/chatkit/threads/{thread_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ThreadDeleteResponse, + ) + + def list_items( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Data, AsyncConversationCursorPage[Data]]: + """ + List ChatKit thread items + + Args: + after: List items created after this thread item ID. Defaults to null for the first + page. + + before: List items created before this thread item ID. Defaults to null for the newest + results. + + limit: Maximum number of thread items to return. Defaults to 20. + + order: Sort order for results by creation time. Defaults to `desc`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not thread_id: + raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") + extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} + return self._get_api_list( + f"/chatkit/threads/{thread_id}/items", + page=AsyncConversationCursorPage[Data], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + thread_list_items_params.ThreadListItemsParams, + ), + ), + model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system + ) + + +class ThreadsWithRawResponse: + def __init__(self, threads: Threads) -> None: + self._threads = threads + + self.retrieve = _legacy_response.to_raw_response_wrapper( + threads.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + threads.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + threads.delete, + ) + self.list_items = _legacy_response.to_raw_response_wrapper( + threads.list_items, + ) + + +class AsyncThreadsWithRawResponse: + def __init__(self, threads: AsyncThreads) -> None: + self._threads = threads + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + threads.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + threads.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + threads.delete, + ) + self.list_items = _legacy_response.async_to_raw_response_wrapper( + threads.list_items, + ) + + +class ThreadsWithStreamingResponse: + def __init__(self, threads: Threads) -> None: + self._threads = threads + + self.retrieve = to_streamed_response_wrapper( + threads.retrieve, + ) + self.list = to_streamed_response_wrapper( + threads.list, + ) + self.delete = to_streamed_response_wrapper( + threads.delete, + ) + self.list_items = to_streamed_response_wrapper( + threads.list_items, + ) + + +class AsyncThreadsWithStreamingResponse: + def __init__(self, threads: AsyncThreads) -> None: + self._threads = threads + + self.retrieve = async_to_streamed_response_wrapper( + threads.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + threads.list, + ) + self.delete = async_to_streamed_response_wrapper( + threads.delete, + ) + self.list_items = async_to_streamed_response_wrapper( + threads.list_items, + ) diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index aae26bab64..265be6f743 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -170,7 +170,8 @@ def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -286,7 +287,8 @@ def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -398,7 +400,8 @@ def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1054,7 +1057,8 @@ async def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1170,7 +1174,8 @@ async def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1282,7 +1287,8 @@ async def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index 7dcea6b5cf..a8c4761717 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -123,6 +123,10 @@ def accept( "gpt-4o-realtime-preview-2025-06-03", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", ], ] | Omit = omit, @@ -428,6 +432,10 @@ async def accept( "gpt-4o-realtime-preview-2025-06-03", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", ], ] | Omit = omit, diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 7b4486d502..bd4e1d9644 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -363,13 +363,14 @@ async def __aenter__(self) -> AsyncRealtimeConnection: extra_query = self.__extra_query await self.__client._refresh_api_key() auth_headers = self.__client.auth_headers + extra_query = self.__extra_query if self.__call_id is not omit: extra_query = {**extra_query, "call_id": self.__call_id} if is_async_azure_client(self.__client): model = self.__model if not model: raise OpenAIError("`model` is required for Azure Realtime API") - else: + else: url, auth_headers = await self.__client._configure_realtime(model, extra_query) else: url = self._prepare_url().copy_with( @@ -551,13 +552,14 @@ def __enter__(self) -> RealtimeConnection: extra_query = self.__extra_query self.__client._refresh_api_key() auth_headers = self.__client.auth_headers + extra_query = self.__extra_query if self.__call_id is not omit: extra_query = {**extra_query, "call_id": self.__call_id} if is_azure_client(self.__client): model = self.__model if not model: raise OpenAIError("`model` is required for Azure Realtime API") - else: + else: url, auth_headers = self.__client._configure_realtime(model, extra_query) else: url = self._prepare_url().copy_with( diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py new file mode 100644 index 0000000000..4df5f02004 --- /dev/null +++ b/src/openai/resources/videos.py @@ -0,0 +1,847 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Mapping, cast +from typing_extensions import Literal, assert_never + +import httpx + +from .. import _legacy_response +from ..types import ( + VideoSize, + VideoModel, + VideoSeconds, + video_list_params, + video_remix_params, + video_create_params, + video_download_content_params, +) +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_streamed_response_wrapper, + async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ..types.video import Video +from .._base_client import AsyncPaginator, make_request_options +from .._utils._utils import is_given +from ..types.video_size import VideoSize +from ..types.video_model import VideoModel +from ..types.video_seconds import VideoSeconds +from ..types.video_delete_response import VideoDeleteResponse + +__all__ = ["Videos", "AsyncVideos"] + + +class Videos(SyncAPIResource): + @cached_property + def with_raw_response(self) -> VideosWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return VideosWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VideosWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return VideosWithStreamingResponse(self) + + def create( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a video + + Args: + prompt: Text prompt that describes the video to generate. + + input_reference: Optional image reference that guides generation. + + model: The video generation model to use. Defaults to `sora-2`. + + seconds: Clip duration in seconds. Defaults to 4 seconds. + + size: Output resolution formatted as width x height. Defaults to 720x1280. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "prompt": prompt, + "input_reference": input_reference, + "model": model, + "seconds": seconds, + "size": size, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos", + body=maybe_transform(body, video_create_params.VideoCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + def create_and_poll( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """Create a video and wait for it to be processed.""" + video = self.create( + model=model, + prompt=prompt, + input_reference=input_reference, + seconds=seconds, + size=size, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return self.poll( + video.id, + poll_interval_ms=poll_interval_ms, + ) + + def poll( + self, + video_id: str, + *, + poll_interval_ms: int | Omit = omit, + ) -> Video: + """Wait for the vector store file to finish processing. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = self.with_raw_response.retrieve( + video_id, + extra_headers=headers, + ) + + video = response.parse() + if video.status == "in_progress" or video.status == "queued": + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + self._sleep(poll_interval_ms / 1000) + elif video.status == "completed" or video.status == "failed": + return video + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(video.status) + else: + return video + + def retrieve( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Retrieve a video + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return self._get( + f"/videos/{video_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Video]: + """ + List videos + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/videos", + page=SyncConversationCursorPage[Video], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + video_list_params.VideoListParams, + ), + ), + model=Video, + ) + + def delete( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VideoDeleteResponse: + """ + Delete a video + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return self._delete( + f"/videos/{video_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VideoDeleteResponse, + ) + + def download_content( + self, + video_id: str, + *, + variant: Literal["video", "thumbnail", "spritesheet"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """Download video content + + Args: + variant: Which downloadable asset to return. + + Defaults to the MP4 video. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + f"/videos/{video_id}/content", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"variant": variant}, video_download_content_params.VideoDownloadContentParams), + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + def remix( + self, + video_id: str, + *, + prompt: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a video remix + + Args: + prompt: Updated text prompt that directs the remix generation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return self._post( + f"/videos/{video_id}/remix", + body=maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + +class AsyncVideos(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncVideosWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncVideosWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVideosWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncVideosWithStreamingResponse(self) + + async def create( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a video + + Args: + prompt: Text prompt that describes the video to generate. + + input_reference: Optional image reference that guides generation. + + model: The video generation model to use. Defaults to `sora-2`. + + seconds: Clip duration in seconds. Defaults to 4 seconds. + + size: Output resolution formatted as width x height. Defaults to 720x1280. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "prompt": prompt, + "input_reference": input_reference, + "model": model, + "seconds": seconds, + "size": size, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos", + body=await async_maybe_transform(body, video_create_params.VideoCreateParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + async def create_and_poll( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """Create a video and wait for it to be processed.""" + video = await self.create( + model=model, + prompt=prompt, + input_reference=input_reference, + seconds=seconds, + size=size, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + return await self.poll( + video.id, + poll_interval_ms=poll_interval_ms, + ) + + async def poll( + self, + video_id: str, + *, + poll_interval_ms: int | Omit = omit, + ) -> Video: + """Wait for the vector store file to finish processing. + + Note: this will return even if the file failed to process, you need to check + file.last_error and file.status to handle these cases + """ + headers: dict[str, str] = {"X-Stainless-Poll-Helper": "true"} + if is_given(poll_interval_ms): + headers["X-Stainless-Custom-Poll-Interval"] = str(poll_interval_ms) + + while True: + response = await self.with_raw_response.retrieve( + video_id, + extra_headers=headers, + ) + + video = response.parse() + if video.status == "in_progress" or video.status == "queued": + if not is_given(poll_interval_ms): + from_header = response.headers.get("openai-poll-after-ms") + if from_header is not None: + poll_interval_ms = int(from_header) + else: + poll_interval_ms = 1000 + + await self._sleep(poll_interval_ms / 1000) + elif video.status == "completed" or video.status == "failed": + return video + else: + if TYPE_CHECKING: # type: ignore[unreachable] + assert_never(video.status) + else: + return video + + async def retrieve( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Retrieve a video + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return await self._get( + f"/videos/{video_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Video, AsyncConversationCursorPage[Video]]: + """ + List videos + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/videos", + page=AsyncConversationCursorPage[Video], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + video_list_params.VideoListParams, + ), + ), + model=Video, + ) + + async def delete( + self, + video_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VideoDeleteResponse: + """ + Delete a video + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return await self._delete( + f"/videos/{video_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VideoDeleteResponse, + ) + + async def download_content( + self, + video_id: str, + *, + variant: Literal["video", "thumbnail", "spritesheet"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """Download video content + + Args: + variant: Which downloadable asset to return. + + Defaults to the MP4 video. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + f"/videos/{video_id}/content", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"variant": variant}, video_download_content_params.VideoDownloadContentParams + ), + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + async def remix( + self, + video_id: str, + *, + prompt: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a video remix + + Args: + prompt: Updated text prompt that directs the remix generation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not video_id: + raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") + return await self._post( + f"/videos/{video_id}/remix", + body=await async_maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + +class VideosWithRawResponse: + def __init__(self, videos: Videos) -> None: + self._videos = videos + + self.create = _legacy_response.to_raw_response_wrapper( + videos.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + videos.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + videos.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + videos.delete, + ) + self.download_content = _legacy_response.to_raw_response_wrapper( + videos.download_content, + ) + self.remix = _legacy_response.to_raw_response_wrapper( + videos.remix, + ) + + +class AsyncVideosWithRawResponse: + def __init__(self, videos: AsyncVideos) -> None: + self._videos = videos + + self.create = _legacy_response.async_to_raw_response_wrapper( + videos.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + videos.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + videos.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + videos.delete, + ) + self.download_content = _legacy_response.async_to_raw_response_wrapper( + videos.download_content, + ) + self.remix = _legacy_response.async_to_raw_response_wrapper( + videos.remix, + ) + + +class VideosWithStreamingResponse: + def __init__(self, videos: Videos) -> None: + self._videos = videos + + self.create = to_streamed_response_wrapper( + videos.create, + ) + self.retrieve = to_streamed_response_wrapper( + videos.retrieve, + ) + self.list = to_streamed_response_wrapper( + videos.list, + ) + self.delete = to_streamed_response_wrapper( + videos.delete, + ) + self.download_content = to_custom_streamed_response_wrapper( + videos.download_content, + StreamedBinaryAPIResponse, + ) + self.remix = to_streamed_response_wrapper( + videos.remix, + ) + + +class AsyncVideosWithStreamingResponse: + def __init__(self, videos: AsyncVideos) -> None: + self._videos = videos + + self.create = async_to_streamed_response_wrapper( + videos.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + videos.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + videos.list, + ) + self.delete = async_to_streamed_response_wrapper( + videos.delete, + ) + self.download_content = async_to_custom_streamed_response_wrapper( + videos.download_content, + AsyncStreamedBinaryAPIResponse, + ) + self.remix = async_to_streamed_response_wrapper( + videos.remix, + ) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 87e4520461..a98ca16ee9 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -5,6 +5,7 @@ from .batch import Batch as Batch from .image import Image as Image from .model import Model as Model +from .video import Video as Video from .shared import ( Metadata as Metadata, AllModels as AllModels, @@ -29,16 +30,19 @@ from .chat_model import ChatModel as ChatModel from .completion import Completion as Completion from .moderation import Moderation as Moderation +from .video_size import VideoSize as VideoSize from .audio_model import AudioModel as AudioModel from .batch_error import BatchError as BatchError from .batch_usage import BatchUsage as BatchUsage from .file_object import FileObject as FileObject from .image_model import ImageModel as ImageModel +from .video_model import VideoModel as VideoModel from .file_content import FileContent as FileContent from .file_deleted import FileDeleted as FileDeleted from .file_purpose import FilePurpose as FilePurpose from .vector_store import VectorStore as VectorStore from .model_deleted import ModelDeleted as ModelDeleted +from .video_seconds import VideoSeconds as VideoSeconds from .embedding_model import EmbeddingModel as EmbeddingModel from .images_response import ImagesResponse as ImagesResponse from .completion_usage import CompletionUsage as CompletionUsage @@ -48,11 +52,15 @@ from .batch_list_params import BatchListParams as BatchListParams from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams +from .video_list_params import VideoListParams as VideoListParams from .eval_create_params import EvalCreateParams as EvalCreateParams from .eval_list_response import EvalListResponse as EvalListResponse from .eval_update_params import EvalUpdateParams as EvalUpdateParams from .file_create_params import FileCreateParams as FileCreateParams +from .video_create_error import VideoCreateError as VideoCreateError +from .video_remix_params import VideoRemixParams as VideoRemixParams from .batch_create_params import BatchCreateParams as BatchCreateParams +from .video_create_params import VideoCreateParams as VideoCreateParams from .batch_request_counts import BatchRequestCounts as BatchRequestCounts from .eval_create_response import EvalCreateResponse as EvalCreateResponse from .eval_delete_response import EvalDeleteResponse as EvalDeleteResponse @@ -62,6 +70,7 @@ from .audio_response_format import AudioResponseFormat as AudioResponseFormat from .container_list_params import ContainerListParams as ContainerListParams from .image_generate_params import ImageGenerateParams as ImageGenerateParams +from .video_delete_response import VideoDeleteResponse as VideoDeleteResponse from .eval_retrieve_response import EvalRetrieveResponse as EvalRetrieveResponse from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy from .image_gen_stream_event import ImageGenStreamEvent as ImageGenStreamEvent @@ -89,6 +98,7 @@ from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams from .image_gen_partial_image_event import ImageGenPartialImageEvent as ImageGenPartialImageEvent from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy +from .video_download_content_params import VideoDownloadContentParams as VideoDownloadContentParams from .eval_custom_data_source_config import EvalCustomDataSourceConfig as EvalCustomDataSourceConfig from .image_edit_partial_image_event import ImageEditPartialImageEvent as ImageEditPartialImageEvent from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index 5ba3eadf3c..9ef6283864 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -4,9 +4,12 @@ from .thread import Thread as Thread from .assistant import Assistant as Assistant +from .file_part import FilePart as FilePart +from .image_part import ImagePart as ImagePart from .function_tool import FunctionTool as FunctionTool from .assistant_tool import AssistantTool as AssistantTool from .thread_deleted import ThreadDeleted as ThreadDeleted +from .chatkit_workflow import ChatKitWorkflow as ChatKitWorkflow from .file_search_tool import FileSearchTool as FileSearchTool from .assistant_deleted import AssistantDeleted as AssistantDeleted from .function_tool_param import FunctionToolParam as FunctionToolParam @@ -20,9 +23,11 @@ from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams +from .chatkit_upload_file_params import ChatKitUploadFileParams as ChatKitUploadFileParams from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption +from .chatkit_upload_file_response import ChatKitUploadFileResponse as ChatKitUploadFileResponse from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption diff --git a/src/openai/types/beta/chatkit/__init__.py b/src/openai/types/beta/chatkit/__init__.py new file mode 100644 index 0000000000..eafed9dd99 --- /dev/null +++ b/src/openai/types/beta/chatkit/__init__.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .chat_session import ChatSession as ChatSession +from .chatkit_thread import ChatKitThread as ChatKitThread +from .chatkit_attachment import ChatKitAttachment as ChatKitAttachment +from .thread_list_params import ThreadListParams as ThreadListParams +from .chat_session_status import ChatSessionStatus as ChatSessionStatus +from .chatkit_widget_item import ChatKitWidgetItem as ChatKitWidgetItem +from .chat_session_history import ChatSessionHistory as ChatSessionHistory +from .session_create_params import SessionCreateParams as SessionCreateParams +from .thread_delete_response import ThreadDeleteResponse as ThreadDeleteResponse +from .chat_session_file_upload import ChatSessionFileUpload as ChatSessionFileUpload +from .chat_session_rate_limits import ChatSessionRateLimits as ChatSessionRateLimits +from .chatkit_thread_item_list import ChatKitThreadItemList as ChatKitThreadItemList +from .thread_list_items_params import ThreadListItemsParams as ThreadListItemsParams +from .chat_session_workflow_param import ChatSessionWorkflowParam as ChatSessionWorkflowParam +from .chatkit_response_output_text import ChatKitResponseOutputText as ChatKitResponseOutputText +from .chat_session_rate_limits_param import ChatSessionRateLimitsParam as ChatSessionRateLimitsParam +from .chat_session_expires_after_param import ChatSessionExpiresAfterParam as ChatSessionExpiresAfterParam +from .chatkit_thread_user_message_item import ChatKitThreadUserMessageItem as ChatKitThreadUserMessageItem +from .chat_session_chatkit_configuration import ChatSessionChatKitConfiguration as ChatSessionChatKitConfiguration +from .chat_session_automatic_thread_titling import ( + ChatSessionAutomaticThreadTitling as ChatSessionAutomaticThreadTitling, +) +from .chatkit_thread_assistant_message_item import ( + ChatKitThreadAssistantMessageItem as ChatKitThreadAssistantMessageItem, +) +from .chat_session_chatkit_configuration_param import ( + ChatSessionChatKitConfigurationParam as ChatSessionChatKitConfigurationParam, +) diff --git a/src/openai/types/beta/chatkit/chat_session.py b/src/openai/types/beta/chatkit/chat_session.py new file mode 100644 index 0000000000..82baea211c --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel +from ..chatkit_workflow import ChatKitWorkflow +from .chat_session_status import ChatSessionStatus +from .chat_session_rate_limits import ChatSessionRateLimits +from .chat_session_chatkit_configuration import ChatSessionChatKitConfiguration + +__all__ = ["ChatSession"] + + +class ChatSession(BaseModel): + id: str + """Identifier for the ChatKit session.""" + + chatkit_configuration: ChatSessionChatKitConfiguration + """Resolved ChatKit feature configuration for the session.""" + + client_secret: str + """Ephemeral client secret that authenticates session requests.""" + + expires_at: int + """Unix timestamp (in seconds) for when the session expires.""" + + max_requests_per_1_minute: int + """Convenience copy of the per-minute request limit.""" + + object: Literal["chatkit.session"] + """Type discriminator that is always `chatkit.session`.""" + + rate_limits: ChatSessionRateLimits + """Resolved rate limit values.""" + + status: ChatSessionStatus + """Current lifecycle state of the session.""" + + user: str + """User identifier associated with the session.""" + + workflow: ChatKitWorkflow + """Workflow metadata for the session.""" diff --git a/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py b/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py new file mode 100644 index 0000000000..4fa96a4433 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["ChatSessionAutomaticThreadTitling"] + + +class ChatSessionAutomaticThreadTitling(BaseModel): + enabled: bool + """Whether automatic thread titling is enabled.""" diff --git a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py new file mode 100644 index 0000000000..6205b172cf --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ...._models import BaseModel +from .chat_session_history import ChatSessionHistory +from .chat_session_file_upload import ChatSessionFileUpload +from .chat_session_automatic_thread_titling import ChatSessionAutomaticThreadTitling + +__all__ = ["ChatSessionChatKitConfiguration"] + + +class ChatSessionChatKitConfiguration(BaseModel): + automatic_thread_titling: ChatSessionAutomaticThreadTitling + """Automatic thread titling preferences.""" + + file_upload: ChatSessionFileUpload + """Upload settings for the session.""" + + history: ChatSessionHistory + """History retention configuration.""" diff --git a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py new file mode 100644 index 0000000000..0a5ae80a76 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChatSessionChatKitConfigurationParam", "AutomaticThreadTitling", "FileUpload", "History"] + + +class AutomaticThreadTitling(TypedDict, total=False): + enabled: bool + """Enable automatic thread title generation. Defaults to true.""" + + +class FileUpload(TypedDict, total=False): + enabled: bool + """Enable uploads for this session. Defaults to false.""" + + max_file_size: int + """Maximum size in megabytes for each uploaded file. + + Defaults to 512 MB, which is the maximum allowable size. + """ + + max_files: int + """Maximum number of files that can be uploaded to the session. Defaults to 10.""" + + +class History(TypedDict, total=False): + enabled: bool + """Enables chat users to access previous ChatKit threads. Defaults to true.""" + + recent_threads: int + """Number of recent ChatKit threads users have access to. + + Defaults to unlimited when unset. + """ + + +class ChatSessionChatKitConfigurationParam(TypedDict, total=False): + automatic_thread_titling: AutomaticThreadTitling + """Configuration for automatic thread titling. + + When omitted, automatic thread titling is enabled by default. + """ + + file_upload: FileUpload + """Configuration for upload enablement and limits. + + When omitted, uploads are disabled by default (max_files 10, max_file_size 512 + MB). + """ + + history: History + """Configuration for chat history retention. + + When omitted, history is enabled by default with no limit on recent_threads + (null). + """ diff --git a/src/openai/types/beta/chatkit/chat_session_expires_after_param.py b/src/openai/types/beta/chatkit/chat_session_expires_after_param.py new file mode 100644 index 0000000000..ceb5a984c5 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_expires_after_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ChatSessionExpiresAfterParam"] + + +class ChatSessionExpiresAfterParam(TypedDict, total=False): + anchor: Required[Literal["created_at"]] + """Base timestamp used to calculate expiration. Currently fixed to `created_at`.""" + + seconds: Required[int] + """Number of seconds after the anchor when the session expires.""" diff --git a/src/openai/types/beta/chatkit/chat_session_file_upload.py b/src/openai/types/beta/chatkit/chat_session_file_upload.py new file mode 100644 index 0000000000..c63c7a0149 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_file_upload.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["ChatSessionFileUpload"] + + +class ChatSessionFileUpload(BaseModel): + enabled: bool + """Indicates if uploads are enabled for the session.""" + + max_file_size: Optional[int] = None + """Maximum upload size in megabytes.""" + + max_files: Optional[int] = None + """Maximum number of uploads allowed during the session.""" diff --git a/src/openai/types/beta/chatkit/chat_session_history.py b/src/openai/types/beta/chatkit/chat_session_history.py new file mode 100644 index 0000000000..66ebe00877 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_history.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["ChatSessionHistory"] + + +class ChatSessionHistory(BaseModel): + enabled: bool + """Indicates if chat history is persisted for the session.""" + + recent_threads: Optional[int] = None + """Number of prior threads surfaced in history views. + + Defaults to null when all history is retained. + """ diff --git a/src/openai/types/beta/chatkit/chat_session_rate_limits.py b/src/openai/types/beta/chatkit/chat_session_rate_limits.py new file mode 100644 index 0000000000..392225e347 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_rate_limits.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["ChatSessionRateLimits"] + + +class ChatSessionRateLimits(BaseModel): + max_requests_per_1_minute: int + """Maximum allowed requests per one-minute window.""" diff --git a/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py b/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py new file mode 100644 index 0000000000..7894c06484 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChatSessionRateLimitsParam"] + + +class ChatSessionRateLimitsParam(TypedDict, total=False): + max_requests_per_1_minute: int + """Maximum number of requests allowed per minute for the session. Defaults to 10.""" diff --git a/src/openai/types/beta/chatkit/chat_session_status.py b/src/openai/types/beta/chatkit/chat_session_status.py new file mode 100644 index 0000000000..a483099c6c --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChatSessionStatus"] + +ChatSessionStatus: TypeAlias = Literal["active", "expired", "cancelled"] diff --git a/src/openai/types/beta/chatkit/chat_session_workflow_param.py b/src/openai/types/beta/chatkit/chat_session_workflow_param.py new file mode 100644 index 0000000000..5542922102 --- /dev/null +++ b/src/openai/types/beta/chatkit/chat_session_workflow_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Required, TypedDict + +__all__ = ["ChatSessionWorkflowParam", "Tracing"] + + +class Tracing(TypedDict, total=False): + enabled: bool + """Whether tracing is enabled during the session. Defaults to true.""" + + +class ChatSessionWorkflowParam(TypedDict, total=False): + id: Required[str] + """Identifier for the workflow invoked by the session.""" + + state_variables: Dict[str, Union[str, bool, float]] + """State variables forwarded to the workflow. + + Keys may be up to 64 characters, values must be primitive types, and the map + defaults to an empty object. + """ + + tracing: Tracing + """Optional tracing overrides for the workflow invocation. + + When omitted, tracing is enabled by default. + """ + + version: str + """Specific workflow version to run. Defaults to the latest deployed version.""" diff --git a/src/openai/types/beta/chatkit/chatkit_attachment.py b/src/openai/types/beta/chatkit/chatkit_attachment.py new file mode 100644 index 0000000000..8d8ad3e128 --- /dev/null +++ b/src/openai/types/beta/chatkit/chatkit_attachment.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ChatKitAttachment"] + + +class ChatKitAttachment(BaseModel): + id: str + """Identifier for the attachment.""" + + mime_type: str + """MIME type of the attachment.""" + + name: str + """Original display name for the attachment.""" + + preview_url: Optional[str] = None + """Preview URL for rendering the attachment inline.""" + + type: Literal["image", "file"] + """Attachment discriminator.""" diff --git a/src/openai/types/beta/chatkit/chatkit_response_output_text.py b/src/openai/types/beta/chatkit/chatkit_response_output_text.py new file mode 100644 index 0000000000..116b797ec2 --- /dev/null +++ b/src/openai/types/beta/chatkit/chatkit_response_output_text.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "ChatKitResponseOutputText", + "Annotation", + "AnnotationFile", + "AnnotationFileSource", + "AnnotationURL", + "AnnotationURLSource", +] + + +class AnnotationFileSource(BaseModel): + filename: str + """Filename referenced by the annotation.""" + + type: Literal["file"] + """Type discriminator that is always `file`.""" + + +class AnnotationFile(BaseModel): + source: AnnotationFileSource + """File attachment referenced by the annotation.""" + + type: Literal["file"] + """Type discriminator that is always `file` for this annotation.""" + + +class AnnotationURLSource(BaseModel): + type: Literal["url"] + """Type discriminator that is always `url`.""" + + url: str + """URL referenced by the annotation.""" + + +class AnnotationURL(BaseModel): + source: AnnotationURLSource + """URL referenced by the annotation.""" + + type: Literal["url"] + """Type discriminator that is always `url` for this annotation.""" + + +Annotation: TypeAlias = Annotated[Union[AnnotationFile, AnnotationURL], PropertyInfo(discriminator="type")] + + +class ChatKitResponseOutputText(BaseModel): + annotations: List[Annotation] + """Ordered list of annotations attached to the response text.""" + + text: str + """Assistant generated text.""" + + type: Literal["output_text"] + """Type discriminator that is always `output_text`.""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread.py b/src/openai/types/beta/chatkit/chatkit_thread.py new file mode 100644 index 0000000000..abd1a9ea01 --- /dev/null +++ b/src/openai/types/beta/chatkit/chatkit_thread.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = ["ChatKitThread", "Status", "StatusActive", "StatusLocked", "StatusClosed"] + + +class StatusActive(BaseModel): + type: Literal["active"] + """Status discriminator that is always `active`.""" + + +class StatusLocked(BaseModel): + reason: Optional[str] = None + """Reason that the thread was locked. Defaults to null when no reason is recorded.""" + + type: Literal["locked"] + """Status discriminator that is always `locked`.""" + + +class StatusClosed(BaseModel): + reason: Optional[str] = None + """Reason that the thread was closed. Defaults to null when no reason is recorded.""" + + type: Literal["closed"] + """Status discriminator that is always `closed`.""" + + +Status: TypeAlias = Annotated[Union[StatusActive, StatusLocked, StatusClosed], PropertyInfo(discriminator="type")] + + +class ChatKitThread(BaseModel): + id: str + """Identifier of the thread.""" + + created_at: int + """Unix timestamp (in seconds) for when the thread was created.""" + + object: Literal["chatkit.thread"] + """Type discriminator that is always `chatkit.thread`.""" + + status: Status + """Current status for the thread. Defaults to `active` for newly created threads.""" + + title: Optional[str] = None + """Optional human-readable title for the thread. + + Defaults to null when no title has been generated. + """ + + user: str + """Free-form string that identifies your end user who owns the thread.""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py b/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py new file mode 100644 index 0000000000..f4afd053b6 --- /dev/null +++ b/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ...._models import BaseModel +from .chatkit_response_output_text import ChatKitResponseOutputText + +__all__ = ["ChatKitThreadAssistantMessageItem"] + + +class ChatKitThreadAssistantMessageItem(BaseModel): + id: str + """Identifier of the thread item.""" + + content: List[ChatKitResponseOutputText] + """Ordered assistant response segments.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.assistant_message"] + """Type discriminator that is always `chatkit.assistant_message`.""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread_item_list.py b/src/openai/types/beta/chatkit/chatkit_thread_item_list.py new file mode 100644 index 0000000000..173bd15055 --- /dev/null +++ b/src/openai/types/beta/chatkit/chatkit_thread_item_list.py @@ -0,0 +1,144 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .chatkit_widget_item import ChatKitWidgetItem +from .chatkit_thread_user_message_item import ChatKitThreadUserMessageItem +from .chatkit_thread_assistant_message_item import ChatKitThreadAssistantMessageItem + +__all__ = [ + "ChatKitThreadItemList", + "Data", + "DataChatKitClientToolCall", + "DataChatKitTask", + "DataChatKitTaskGroup", + "DataChatKitTaskGroupTask", +] + + +class DataChatKitClientToolCall(BaseModel): + id: str + """Identifier of the thread item.""" + + arguments: str + """JSON-encoded arguments that were sent to the tool.""" + + call_id: str + """Identifier for the client tool call.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + name: str + """Tool name that was invoked.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + output: Optional[str] = None + """JSON-encoded output captured from the tool. + + Defaults to null while execution is in progress. + """ + + status: Literal["in_progress", "completed"] + """Execution status for the tool call.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.client_tool_call"] + """Type discriminator that is always `chatkit.client_tool_call`.""" + + +class DataChatKitTask(BaseModel): + id: str + """Identifier of the thread item.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + heading: Optional[str] = None + """Optional heading for the task. Defaults to null when not provided.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + summary: Optional[str] = None + """Optional summary that describes the task. Defaults to null when omitted.""" + + task_type: Literal["custom", "thought"] + """Subtype for the task.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.task"] + """Type discriminator that is always `chatkit.task`.""" + + +class DataChatKitTaskGroupTask(BaseModel): + heading: Optional[str] = None + """Optional heading for the grouped task. Defaults to null when not provided.""" + + summary: Optional[str] = None + """Optional summary that describes the grouped task. + + Defaults to null when omitted. + """ + + type: Literal["custom", "thought"] + """Subtype for the grouped task.""" + + +class DataChatKitTaskGroup(BaseModel): + id: str + """Identifier of the thread item.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + tasks: List[DataChatKitTaskGroupTask] + """Tasks included in the group.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.task_group"] + """Type discriminator that is always `chatkit.task_group`.""" + + +Data: TypeAlias = Annotated[ + Union[ + ChatKitThreadUserMessageItem, + ChatKitThreadAssistantMessageItem, + ChatKitWidgetItem, + DataChatKitClientToolCall, + DataChatKitTask, + DataChatKitTaskGroup, + ], + PropertyInfo(discriminator="type"), +] + + +class ChatKitThreadItemList(BaseModel): + data: List[Data] + """A list of items""" + + first_id: Optional[str] = None + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: Optional[str] = None + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py b/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py new file mode 100644 index 0000000000..233d07232f --- /dev/null +++ b/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel +from .chatkit_attachment import ChatKitAttachment + +__all__ = [ + "ChatKitThreadUserMessageItem", + "Content", + "ContentInputText", + "ContentQuotedText", + "InferenceOptions", + "InferenceOptionsToolChoice", +] + + +class ContentInputText(BaseModel): + text: str + """Plain-text content supplied by the user.""" + + type: Literal["input_text"] + """Type discriminator that is always `input_text`.""" + + +class ContentQuotedText(BaseModel): + text: str + """Quoted text content.""" + + type: Literal["quoted_text"] + """Type discriminator that is always `quoted_text`.""" + + +Content: TypeAlias = Annotated[Union[ContentInputText, ContentQuotedText], PropertyInfo(discriminator="type")] + + +class InferenceOptionsToolChoice(BaseModel): + id: str + """Identifier of the requested tool.""" + + +class InferenceOptions(BaseModel): + model: Optional[str] = None + """Model name that generated the response. + + Defaults to null when using the session default. + """ + + tool_choice: Optional[InferenceOptionsToolChoice] = None + """Preferred tool to invoke. Defaults to null when ChatKit should auto-select.""" + + +class ChatKitThreadUserMessageItem(BaseModel): + id: str + """Identifier of the thread item.""" + + attachments: List[ChatKitAttachment] + """Attachments associated with the user message. Defaults to an empty list.""" + + content: List[Content] + """Ordered content elements supplied by the user.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + inference_options: Optional[InferenceOptions] = None + """Inference overrides applied to the message. Defaults to null when unset.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.user_message"] diff --git a/src/openai/types/beta/chatkit/chatkit_widget_item.py b/src/openai/types/beta/chatkit/chatkit_widget_item.py new file mode 100644 index 0000000000..c7f182259a --- /dev/null +++ b/src/openai/types/beta/chatkit/chatkit_widget_item.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ChatKitWidgetItem"] + + +class ChatKitWidgetItem(BaseModel): + id: str + """Identifier of the thread item.""" + + created_at: int + """Unix timestamp (in seconds) for when the item was created.""" + + object: Literal["chatkit.thread_item"] + """Type discriminator that is always `chatkit.thread_item`.""" + + thread_id: str + """Identifier of the parent thread.""" + + type: Literal["chatkit.widget"] + """Type discriminator that is always `chatkit.widget`.""" + + widget: str + """Serialized widget payload rendered in the UI.""" diff --git a/src/openai/types/beta/chatkit/session_create_params.py b/src/openai/types/beta/chatkit/session_create_params.py new file mode 100644 index 0000000000..1803d18cf6 --- /dev/null +++ b/src/openai/types/beta/chatkit/session_create_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .chat_session_workflow_param import ChatSessionWorkflowParam +from .chat_session_rate_limits_param import ChatSessionRateLimitsParam +from .chat_session_expires_after_param import ChatSessionExpiresAfterParam +from .chat_session_chatkit_configuration_param import ChatSessionChatKitConfigurationParam + +__all__ = ["SessionCreateParams"] + + +class SessionCreateParams(TypedDict, total=False): + user: Required[str] + """ + A free-form string that identifies your end user; ensures this Session can + access other objects that have the same `user` scope. + """ + + workflow: Required[ChatSessionWorkflowParam] + """Workflow that powers the session.""" + + chatkit_configuration: ChatSessionChatKitConfigurationParam + """Optional overrides for ChatKit runtime configuration features""" + + expires_after: ChatSessionExpiresAfterParam + """Optional override for session expiration timing in seconds from creation. + + Defaults to 10 minutes. + """ + + rate_limits: ChatSessionRateLimitsParam + """Optional override for per-minute request limits. When omitted, defaults to 10.""" diff --git a/src/openai/types/beta/chatkit/thread_delete_response.py b/src/openai/types/beta/chatkit/thread_delete_response.py new file mode 100644 index 0000000000..03fdec9c2c --- /dev/null +++ b/src/openai/types/beta/chatkit/thread_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ThreadDeleteResponse"] + + +class ThreadDeleteResponse(BaseModel): + id: str + """Identifier of the deleted thread.""" + + deleted: bool + """Indicates that the thread has been deleted.""" + + object: Literal["chatkit.thread.deleted"] + """Type discriminator that is always `chatkit.thread.deleted`.""" diff --git a/src/openai/types/beta/chatkit/thread_list_items_params.py b/src/openai/types/beta/chatkit/thread_list_items_params.py new file mode 100644 index 0000000000..95c959d719 --- /dev/null +++ b/src/openai/types/beta/chatkit/thread_list_items_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ThreadListItemsParams"] + + +class ThreadListItemsParams(TypedDict, total=False): + after: str + """List items created after this thread item ID. + + Defaults to null for the first page. + """ + + before: str + """List items created before this thread item ID. + + Defaults to null for the newest results. + """ + + limit: int + """Maximum number of thread items to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for results by creation time. Defaults to `desc`.""" diff --git a/src/openai/types/beta/chatkit/thread_list_params.py b/src/openai/types/beta/chatkit/thread_list_params.py new file mode 100644 index 0000000000..bb759c7ea3 --- /dev/null +++ b/src/openai/types/beta/chatkit/thread_list_params.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["ThreadListParams"] + + +class ThreadListParams(TypedDict, total=False): + after: str + """List items created after this thread item ID. + + Defaults to null for the first page. + """ + + before: str + """List items created before this thread item ID. + + Defaults to null for the newest results. + """ + + limit: int + """Maximum number of thread items to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for results by creation time. Defaults to `desc`.""" + + user: str + """Filter threads that belong to this user identifier. + + Defaults to null to return all users. + """ diff --git a/src/openai/types/beta/chatkit_upload_file_params.py b/src/openai/types/beta/chatkit_upload_file_params.py new file mode 100644 index 0000000000..87dc993664 --- /dev/null +++ b/src/openai/types/beta/chatkit_upload_file_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ..._types import FileTypes + +__all__ = ["ChatKitUploadFileParams"] + + +class ChatKitUploadFileParams(TypedDict, total=False): + file: Required[FileTypes] + """Binary file contents to store with the ChatKit session. + + Supports PDFs and PNG, JPG, JPEG, GIF, or WEBP images. + """ diff --git a/src/openai/types/beta/chatkit_upload_file_response.py b/src/openai/types/beta/chatkit_upload_file_response.py new file mode 100644 index 0000000000..9527df76fb --- /dev/null +++ b/src/openai/types/beta/chatkit_upload_file_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .file_part import FilePart +from .image_part import ImagePart + +__all__ = ["ChatKitUploadFileResponse"] + +ChatKitUploadFileResponse: TypeAlias = Annotated[Union[FilePart, ImagePart], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/chatkit_workflow.py b/src/openai/types/beta/chatkit_workflow.py new file mode 100644 index 0000000000..00fbcf41ce --- /dev/null +++ b/src/openai/types/beta/chatkit_workflow.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional + +from ..._models import BaseModel + +__all__ = ["ChatKitWorkflow", "Tracing"] + + +class Tracing(BaseModel): + enabled: bool + """Indicates whether tracing is enabled.""" + + +class ChatKitWorkflow(BaseModel): + id: str + """Identifier of the workflow backing the session.""" + + state_variables: Optional[Dict[str, Union[str, bool, float]]] = None + """State variable key-value pairs applied when invoking the workflow. + + Defaults to null when no overrides were provided. + """ + + tracing: Tracing + """Tracing settings applied to the workflow.""" + + version: Optional[str] = None + """Specific workflow version used for the session. + + Defaults to null when using the latest deployment. + """ diff --git a/src/openai/types/beta/file_part.py b/src/openai/types/beta/file_part.py new file mode 100644 index 0000000000..cf60bddc99 --- /dev/null +++ b/src/openai/types/beta/file_part.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FilePart"] + + +class FilePart(BaseModel): + id: str + """Unique identifier for the uploaded file.""" + + mime_type: Optional[str] = None + """MIME type reported for the uploaded file. Defaults to null when unknown.""" + + name: Optional[str] = None + """Original filename supplied by the uploader. Defaults to null when unnamed.""" + + type: Literal["file"] + """Type discriminator that is always `file`.""" + + upload_url: Optional[str] = None + """Signed URL for downloading the uploaded file. + + Defaults to null when no download link is available. + """ diff --git a/src/openai/types/beta/image_part.py b/src/openai/types/beta/image_part.py new file mode 100644 index 0000000000..4c06b4730b --- /dev/null +++ b/src/openai/types/beta/image_part.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ImagePart"] + + +class ImagePart(BaseModel): + id: str + """Unique identifier for the uploaded image.""" + + mime_type: str + """MIME type of the uploaded image.""" + + name: Optional[str] = None + """Original filename for the uploaded image. Defaults to null when unnamed.""" + + preview_url: str + """Preview URL that can be rendered inline for the image.""" + + type: Literal["image"] + """Type discriminator that is always `image`.""" + + upload_url: Optional[str] = None + """Signed URL for downloading the uploaded image. + + Defaults to null when no download link is available. + """ diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 065d9789fc..2a8fab0f20 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -30,11 +30,11 @@ class ImageEditParamsBase(TypedDict, total=False): """ background: Optional[Literal["transparent", "opaque", "auto"]] - """Allows to set transparency for the background of the generated image(s). - - This parameter is only supported for `gpt-image-1`. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + """ + Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -44,7 +44,8 @@ class ImageEditParamsBase(TypedDict, total=False): """ Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. """ mask: FileTypes diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index e9e9292cc2..3270ca1d6e 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -19,11 +19,11 @@ class ImageGenerateParamsBase(TypedDict, total=False): """ background: Optional[Literal["transparent", "opaque", "auto"]] - """Allows to set transparency for the background of the generated image(s). - - This parameter is only supported for `gpt-image-1`. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + """ + Allows to set transparency for the background of the generated image(s). This + parameter is only supported for `gpt-image-1`. Must be one of `transparent`, + `opaque` or `auto` (default value). When `auto` is used, the model will + automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py index 7fed69ed82..22b1281fa9 100644 --- a/src/openai/types/image_model.py +++ b/src/openai/types/image_model.py @@ -4,4 +4,4 @@ __all__ = ["ImageModel"] -ImageModel: TypeAlias = Literal["dall-e-2", "dall-e-3", "gpt-image-1"] +ImageModel: TypeAlias = Literal["dall-e-2", "dall-e-3", "gpt-image-1", "gpt-image-1-mini"] diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index 1780572e89..0cfb01e7cf 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -63,6 +63,10 @@ class CallAcceptParams(TypedDict, total=False): "gpt-4o-realtime-preview-2025-06-03", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", ], ] """The Realtime model used for this session.""" diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 755dbe8638..bc205bd3b5 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -62,6 +62,10 @@ class RealtimeSessionCreateRequest(BaseModel): "gpt-4o-realtime-preview-2025-06-03", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", ], None, ] = None diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index cd4ef71ba2..d1fa2b35d2 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -63,6 +63,10 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): "gpt-4o-realtime-preview-2025-06-03", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", ], ] """The Realtime model used for this session.""" diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 2d6912d072..bb6b94e900 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -415,6 +415,10 @@ class RealtimeSessionCreateResponse(BaseModel): "gpt-4o-realtime-preview-2025-06-03", "gpt-4o-mini-realtime-preview", "gpt-4o-mini-realtime-preview-2024-12-17", + "gpt-realtime-mini", + "gpt-realtime-mini-2025-10-06", + "gpt-audio-mini", + "gpt-audio-mini-2025-10-06", ], None, ] = None diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 8dd2bd5981..6239b818c9 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -199,7 +199,8 @@ class ImageGeneration(BaseModel): """ Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. """ input_image_mask: Optional[ImageGenerationInputImageMask] = None @@ -208,7 +209,7 @@ class ImageGeneration(BaseModel): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Optional[Literal["gpt-image-1"]] = None + model: Optional[Literal["gpt-image-1", "gpt-image-1-mini"]] = None """The image generation model to use. Default: `gpt-image-1`.""" moderation: Optional[Literal["auto", "low"]] = None diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index e84abc4390..ff4ac2b953 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -199,7 +199,8 @@ class ImageGeneration(TypedDict, total=False): """ Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Supports `high` and `low`. Defaults to `low`. + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. """ input_image_mask: ImageGenerationInputImageMask @@ -208,7 +209,7 @@ class ImageGeneration(TypedDict, total=False): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Literal["gpt-image-1"] + model: Literal["gpt-image-1", "gpt-image-1-mini"] """The image generation model to use. Default: `gpt-image-1`.""" moderation: Literal["auto", "low"] diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py index 76ca1ffd29..3e0b09e2d1 100644 --- a/src/openai/types/shared/all_models.py +++ b/src/openai/types/shared/all_models.py @@ -22,5 +22,7 @@ "computer-use-preview", "computer-use-preview-2025-03-11", "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", ], ] diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py index 4fbdce8db9..432cb82afd 100644 --- a/src/openai/types/shared/responses_model.py +++ b/src/openai/types/shared/responses_model.py @@ -22,5 +22,7 @@ "computer-use-preview", "computer-use-preview-2025-03-11", "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", ], ] diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py index 2feaa22b67..fe34eb0f62 100644 --- a/src/openai/types/shared_params/responses_model.py +++ b/src/openai/types/shared_params/responses_model.py @@ -24,5 +24,7 @@ "computer-use-preview", "computer-use-preview-2025-03-11", "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", ], ] diff --git a/src/openai/types/video.py b/src/openai/types/video.py new file mode 100644 index 0000000000..2c804f75b8 --- /dev/null +++ b/src/openai/types/video.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .._models import BaseModel +from .video_size import VideoSize +from .video_model import VideoModel +from .video_seconds import VideoSeconds +from .video_create_error import VideoCreateError + +__all__ = ["Video"] + + +class Video(BaseModel): + id: str + """Unique identifier for the video job.""" + + completed_at: Optional[int] = None + """Unix timestamp (seconds) for when the job completed, if finished.""" + + created_at: int + """Unix timestamp (seconds) for when the job was created.""" + + error: Optional[VideoCreateError] = None + """Error payload that explains why generation failed, if applicable.""" + + expires_at: Optional[int] = None + """Unix timestamp (seconds) for when the downloadable assets expire, if set.""" + + model: VideoModel + """The video generation model that produced the job.""" + + object: Literal["video"] + """The object type, which is always `video`.""" + + progress: int + """Approximate completion percentage for the generation task.""" + + remixed_from_video_id: Optional[str] = None + """Identifier of the source video if this video is a remix.""" + + seconds: VideoSeconds + """Duration of the generated clip in seconds.""" + + size: VideoSize + """The resolution of the generated video.""" + + status: Literal["queued", "in_progress", "completed", "failed"] + """Current lifecycle status of the video job.""" diff --git a/src/openai/types/video_create_error.py b/src/openai/types/video_create_error.py new file mode 100644 index 0000000000..ae328b78ea --- /dev/null +++ b/src/openai/types/video_create_error.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["VideoCreateError"] + + +class VideoCreateError(BaseModel): + code: str + + message: str diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py new file mode 100644 index 0000000000..527d62d193 --- /dev/null +++ b/src/openai/types/video_create_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes +from .video_size import VideoSize +from .video_model import VideoModel +from .video_seconds import VideoSeconds + +__all__ = ["VideoCreateParams"] + + +class VideoCreateParams(TypedDict, total=False): + prompt: Required[str] + """Text prompt that describes the video to generate.""" + + input_reference: FileTypes + """Optional image reference that guides generation.""" + + model: VideoModel + """The video generation model to use. Defaults to `sora-2`.""" + + seconds: VideoSeconds + """Clip duration in seconds. Defaults to 4 seconds.""" + + size: VideoSize + """Output resolution formatted as width x height. Defaults to 720x1280.""" diff --git a/src/openai/types/video_delete_response.py b/src/openai/types/video_delete_response.py new file mode 100644 index 0000000000..e2673ffe2b --- /dev/null +++ b/src/openai/types/video_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["VideoDeleteResponse"] + + +class VideoDeleteResponse(BaseModel): + id: str + """Identifier of the deleted video.""" + + deleted: bool + """Indicates that the video resource was deleted.""" + + object: Literal["video.deleted"] + """The object type that signals the deletion response.""" diff --git a/src/openai/types/video_download_content_params.py b/src/openai/types/video_download_content_params.py new file mode 100644 index 0000000000..8c113d6715 --- /dev/null +++ b/src/openai/types/video_download_content_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VideoDownloadContentParams"] + + +class VideoDownloadContentParams(TypedDict, total=False): + variant: Literal["video", "thumbnail", "spritesheet"] + """Which downloadable asset to return. Defaults to the MP4 video.""" diff --git a/src/openai/types/video_list_params.py b/src/openai/types/video_list_params.py new file mode 100644 index 0000000000..bf55ba7fa2 --- /dev/null +++ b/src/openai/types/video_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VideoListParams"] + + +class VideoListParams(TypedDict, total=False): + after: str + """Identifier for the last item from the previous pagination request""" + + limit: int + """Number of items to retrieve""" + + order: Literal["asc", "desc"] + """Sort order of results by timestamp. + + Use `asc` for ascending order or `desc` for descending order. + """ diff --git a/src/openai/types/video_model.py b/src/openai/types/video_model.py new file mode 100644 index 0000000000..0b0835fca4 --- /dev/null +++ b/src/openai/types/video_model.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoModel"] + +VideoModel: TypeAlias = Literal["sora-2", "sora-2-pro"] diff --git a/src/openai/types/video_remix_params.py b/src/openai/types/video_remix_params.py new file mode 100644 index 0000000000..15388d6172 --- /dev/null +++ b/src/openai/types/video_remix_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["VideoRemixParams"] + + +class VideoRemixParams(TypedDict, total=False): + prompt: Required[str] + """Updated text prompt that directs the remix generation.""" diff --git a/src/openai/types/video_seconds.py b/src/openai/types/video_seconds.py new file mode 100644 index 0000000000..e50d37dc51 --- /dev/null +++ b/src/openai/types/video_seconds.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoSeconds"] + +VideoSeconds: TypeAlias = Literal["4", "8", "12"] diff --git a/src/openai/types/video_size.py b/src/openai/types/video_size.py new file mode 100644 index 0000000000..215ac8815a --- /dev/null +++ b/src/openai/types/video_size.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoSize"] + +VideoSize: TypeAlias = Literal["720x1280", "1280x720", "1024x1792", "1792x1024"] diff --git a/tests/api_resources/beta/chatkit/__init__.py b/tests/api_resources/beta/chatkit/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/beta/chatkit/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/chatkit/test_sessions.py b/tests/api_resources/beta/chatkit/test_sessions.py new file mode 100644 index 0000000000..c94e4c92ae --- /dev/null +++ b/tests/api_resources/beta/chatkit/test_sessions.py @@ -0,0 +1,230 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.beta.chatkit import ( + ChatSession, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSessions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + session = client.beta.chatkit.sessions.create( + user="x", + workflow={"id": "id"}, + ) + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + session = client.beta.chatkit.sessions.create( + user="x", + workflow={ + "id": "id", + "state_variables": {"foo": "string"}, + "tracing": {"enabled": True}, + "version": "version", + }, + chatkit_configuration={ + "automatic_thread_titling": {"enabled": True}, + "file_upload": { + "enabled": True, + "max_file_size": 1, + "max_files": 1, + }, + "history": { + "enabled": True, + "recent_threads": 1, + }, + }, + expires_after={ + "anchor": "created_at", + "seconds": 1, + }, + rate_limits={"max_requests_per_1_minute": 1}, + ) + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.beta.chatkit.sessions.with_raw_response.create( + user="x", + workflow={"id": "id"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.beta.chatkit.sessions.with_streaming_response.create( + user="x", + workflow={"id": "id"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + session = client.beta.chatkit.sessions.cancel( + "cksess_123", + ) + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + response = client.beta.chatkit.sessions.with_raw_response.cancel( + "cksess_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.beta.chatkit.sessions.with_streaming_response.cancel( + "cksess_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): + client.beta.chatkit.sessions.with_raw_response.cancel( + "", + ) + + +class TestAsyncSessions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + session = await async_client.beta.chatkit.sessions.create( + user="x", + workflow={"id": "id"}, + ) + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + session = await async_client.beta.chatkit.sessions.create( + user="x", + workflow={ + "id": "id", + "state_variables": {"foo": "string"}, + "tracing": {"enabled": True}, + "version": "version", + }, + chatkit_configuration={ + "automatic_thread_titling": {"enabled": True}, + "file_upload": { + "enabled": True, + "max_file_size": 1, + "max_files": 1, + }, + "history": { + "enabled": True, + "recent_threads": 1, + }, + }, + expires_after={ + "anchor": "created_at", + "seconds": 1, + }, + rate_limits={"max_requests_per_1_minute": 1}, + ) + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.chatkit.sessions.with_raw_response.create( + user="x", + workflow={"id": "id"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.chatkit.sessions.with_streaming_response.create( + user="x", + workflow={"id": "id"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + session = await async_client.beta.chatkit.sessions.cancel( + "cksess_123", + ) + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.chatkit.sessions.with_raw_response.cancel( + "cksess_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.chatkit.sessions.with_streaming_response.cancel( + "cksess_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(ChatSession, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): + await async_client.beta.chatkit.sessions.with_raw_response.cancel( + "", + ) diff --git a/tests/api_resources/beta/chatkit/test_threads.py b/tests/api_resources/beta/chatkit/test_threads.py new file mode 100644 index 0000000000..6395b72b2f --- /dev/null +++ b/tests/api_resources/beta/chatkit/test_threads.py @@ -0,0 +1,348 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.beta.chatkit import ChatKitThread, ThreadDeleteResponse +from openai.types.beta.chatkit.chatkit_thread_item_list import Data + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestThreads: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + thread = client.beta.chatkit.threads.retrieve( + "cthr_123", + ) + assert_matches_type(ChatKitThread, thread, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.beta.chatkit.threads.with_raw_response.retrieve( + "cthr_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(ChatKitThread, thread, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.beta.chatkit.threads.with_streaming_response.retrieve( + "cthr_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(ChatKitThread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.chatkit.threads.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + thread = client.beta.chatkit.threads.list() + assert_matches_type(SyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + thread = client.beta.chatkit.threads.list( + after="after", + before="before", + limit=0, + order="asc", + user="x", + ) + assert_matches_type(SyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.chatkit.threads.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(SyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.chatkit.threads.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(SyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + thread = client.beta.chatkit.threads.delete( + "cthr_123", + ) + assert_matches_type(ThreadDeleteResponse, thread, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.beta.chatkit.threads.with_raw_response.delete( + "cthr_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(ThreadDeleteResponse, thread, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.beta.chatkit.threads.with_streaming_response.delete( + "cthr_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(ThreadDeleteResponse, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.chatkit.threads.with_raw_response.delete( + "", + ) + + @parametrize + def test_method_list_items(self, client: OpenAI) -> None: + thread = client.beta.chatkit.threads.list_items( + thread_id="cthr_123", + ) + assert_matches_type(SyncConversationCursorPage[Data], thread, path=["response"]) + + @parametrize + def test_method_list_items_with_all_params(self, client: OpenAI) -> None: + thread = client.beta.chatkit.threads.list_items( + thread_id="cthr_123", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[Data], thread, path=["response"]) + + @parametrize + def test_raw_response_list_items(self, client: OpenAI) -> None: + response = client.beta.chatkit.threads.with_raw_response.list_items( + thread_id="cthr_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(SyncConversationCursorPage[Data], thread, path=["response"]) + + @parametrize + def test_streaming_response_list_items(self, client: OpenAI) -> None: + with client.beta.chatkit.threads.with_streaming_response.list_items( + thread_id="cthr_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = response.parse() + assert_matches_type(SyncConversationCursorPage[Data], thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list_items(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + client.beta.chatkit.threads.with_raw_response.list_items( + thread_id="", + ) + + +class TestAsyncThreads: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.chatkit.threads.retrieve( + "cthr_123", + ) + assert_matches_type(ChatKitThread, thread, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.chatkit.threads.with_raw_response.retrieve( + "cthr_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(ChatKitThread, thread, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.chatkit.threads.with_streaming_response.retrieve( + "cthr_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(ChatKitThread, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await async_client.beta.chatkit.threads.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.chatkit.threads.list() + assert_matches_type(AsyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.chatkit.threads.list( + after="after", + before="before", + limit=0, + order="asc", + user="x", + ) + assert_matches_type(AsyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.chatkit.threads.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(AsyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.chatkit.threads.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ChatKitThread], thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.chatkit.threads.delete( + "cthr_123", + ) + assert_matches_type(ThreadDeleteResponse, thread, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.chatkit.threads.with_raw_response.delete( + "cthr_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(ThreadDeleteResponse, thread, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.chatkit.threads.with_streaming_response.delete( + "cthr_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(ThreadDeleteResponse, thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await async_client.beta.chatkit.threads.with_raw_response.delete( + "", + ) + + @parametrize + async def test_method_list_items(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.chatkit.threads.list_items( + thread_id="cthr_123", + ) + assert_matches_type(AsyncConversationCursorPage[Data], thread, path=["response"]) + + @parametrize + async def test_method_list_items_with_all_params(self, async_client: AsyncOpenAI) -> None: + thread = await async_client.beta.chatkit.threads.list_items( + thread_id="cthr_123", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[Data], thread, path=["response"]) + + @parametrize + async def test_raw_response_list_items(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.chatkit.threads.with_raw_response.list_items( + thread_id="cthr_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + thread = response.parse() + assert_matches_type(AsyncConversationCursorPage[Data], thread, path=["response"]) + + @parametrize + async def test_streaming_response_list_items(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.chatkit.threads.with_streaming_response.list_items( + thread_id="cthr_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + thread = await response.parse() + assert_matches_type(AsyncConversationCursorPage[Data], thread, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list_items(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): + await async_client.beta.chatkit.threads.with_raw_response.list_items( + thread_id="", + ) diff --git a/tests/api_resources/beta/test_chatkit.py b/tests/api_resources/beta/test_chatkit.py new file mode 100644 index 0000000000..b05be0ece5 --- /dev/null +++ b/tests/api_resources/beta/test_chatkit.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.beta import ChatKitUploadFileResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestChatKit: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_upload_file(self, client: OpenAI) -> None: + chatkit = client.beta.chatkit.upload_file( + file=b"raw file contents", + ) + assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) + + @parametrize + def test_raw_response_upload_file(self, client: OpenAI) -> None: + response = client.beta.chatkit.with_raw_response.upload_file( + file=b"raw file contents", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chatkit = response.parse() + assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) + + @parametrize + def test_streaming_response_upload_file(self, client: OpenAI) -> None: + with client.beta.chatkit.with_streaming_response.upload_file( + file=b"raw file contents", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chatkit = response.parse() + assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncChatKit: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_upload_file(self, async_client: AsyncOpenAI) -> None: + chatkit = await async_client.beta.chatkit.upload_file( + file=b"raw file contents", + ) + assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) + + @parametrize + async def test_raw_response_upload_file(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.chatkit.with_raw_response.upload_file( + file=b"raw file contents", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + chatkit = response.parse() + assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) + + @parametrize + async def test_streaming_response_upload_file(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.chatkit.with_streaming_response.upload_file( + file=b"raw file contents", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + chatkit = await response.parse() + assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py new file mode 100644 index 0000000000..623cfc2153 --- /dev/null +++ b/tests/api_resources/test_videos.py @@ -0,0 +1,551 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import httpx +import pytest +from respx import MockRouter + +import openai._legacy_response as _legacy_response +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import ( + Video, + VideoDeleteResponse, +) +from openai._utils import assert_signatures_in_sync +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage + +# pyright: reportDeprecated=false + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestVideos: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + video = client.videos.create( + prompt="x", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + video = client.videos.create( + prompt="x", + input_reference=b"raw file contents", + model="sora-2", + seconds="4", + size="720x1280", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.create( + prompt="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.create( + prompt="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + video = client.videos.retrieve( + "video_123", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.retrieve( + "video_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.retrieve( + "video_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + client.videos.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + video = client.videos.list() + assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + video = client.videos.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(SyncConversationCursorPage[Video], video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + video = client.videos.delete( + "video_123", + ) + assert_matches_type(VideoDeleteResponse, video, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.delete( + "video_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(VideoDeleteResponse, video, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.delete( + "video_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(VideoDeleteResponse, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + client.videos.with_raw_response.delete( + "", + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_download_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + video = client.videos.download_content( + video_id="video_123", + ) + assert isinstance(video, _legacy_response.HttpxBinaryResponseContent) + assert video.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_download_content_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + video = client.videos.download_content( + video_id="video_123", + variant="video", + ) + assert isinstance(video, _legacy_response.HttpxBinaryResponseContent) + assert video.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_download_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.videos.with_raw_response.download_content( + video_id="video_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, video, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_download_content(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + with client.videos.with_streaming_response.download_content( + video_id="video_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(bytes, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_download_content(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + client.videos.with_raw_response.download_content( + video_id="", + ) + + @parametrize + def test_method_remix(self, client: OpenAI) -> None: + video = client.videos.remix( + video_id="video_123", + prompt="x", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_raw_response_remix(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.remix( + video_id="video_123", + prompt="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_streaming_response_remix(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.remix( + video_id="video_123", + prompt="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_remix(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + client.videos.with_raw_response.remix( + video_id="", + prompt="x", + ) + + +class TestAsyncVideos: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.create( + prompt="x", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.create( + prompt="x", + input_reference=b"raw file contents", + model="sora-2", + seconds="4", + size="720x1280", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.create( + prompt="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.create( + prompt="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.retrieve( + "video_123", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.retrieve( + "video_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.retrieve( + "video_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + await async_client.videos.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.list() + assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(AsyncConversationCursorPage[Video], video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.delete( + "video_123", + ) + assert_matches_type(VideoDeleteResponse, video, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.delete( + "video_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(VideoDeleteResponse, video, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.delete( + "video_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(VideoDeleteResponse, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + await async_client.videos.with_raw_response.delete( + "", + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_download_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + video = await async_client.videos.download_content( + video_id="video_123", + ) + assert isinstance(video, _legacy_response.HttpxBinaryResponseContent) + assert video.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_download_content_with_all_params( + self, async_client: AsyncOpenAI, respx_mock: MockRouter + ) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + video = await async_client.videos.download_content( + video_id="video_123", + variant="video", + ) + assert isinstance(video, _legacy_response.HttpxBinaryResponseContent) + assert video.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_download_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.videos.with_raw_response.download_content( + video_id="video_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, video, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_download_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/videos/video_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + async with async_client.videos.with_streaming_response.download_content( + video_id="video_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(bytes, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_download_content(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + await async_client.videos.with_raw_response.download_content( + video_id="", + ) + + @parametrize + async def test_method_remix(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.remix( + video_id="video_123", + prompt="x", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_raw_response_remix(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.remix( + video_id="video_123", + prompt="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_streaming_response_remix(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.remix( + video_id="video_123", + prompt="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_remix(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `video_id` but received ''"): + await async_client.videos.with_raw_response.remix( + video_id="", + prompt="x", + ) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + assert_signatures_in_sync( + checking_client.videos.create, + checking_client.videos.create_and_poll, + exclude_params={"extra_headers", "extra_query", "extra_body", "timeout"}, + ) From 8082367a4d06b4c59b06ff417d458a5b22670d07 Mon Sep 17 00:00:00 2001 From: David Meadows Date: Mon, 6 Oct 2025 13:56:31 -0400 Subject: [PATCH 129/408] fix(client): add chatkit to beta resource --- src/openai/resources/beta/beta.py | 11 ++++++----- src/openai/resources/realtime/realtime.py | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 81a6e7aa93..edf30be005 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -28,18 +28,16 @@ ThreadsWithStreamingResponse, AsyncThreadsWithStreamingResponse, ) -from ...resources.chat import Chat, AsyncChat -from .realtime.realtime import ( - Realtime, - AsyncRealtime, -) __all__ = ["Beta", "AsyncBeta"] class Beta(SyncAPIResource): @cached_property + def chatkit(self) -> ChatKit: + return ChatKit(self._client) + @cached_property def assistants(self) -> Assistants: return Assistants(self._client) @@ -69,7 +67,10 @@ def with_streaming_response(self) -> BetaWithStreamingResponse: class AsyncBeta(AsyncAPIResource): @cached_property + def chatkit(self) -> AsyncChatKit: + return AsyncChatKit(self._client) + @cached_property def assistants(self) -> AsyncAssistants: return AsyncAssistants(self._client) diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index bd4e1d9644..6e69258616 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -67,6 +67,7 @@ def client_secrets(self) -> ClientSecrets: @cached_property def calls(self) -> Calls: from ...lib._realtime import _Calls + return _Calls(self._client) @cached_property @@ -126,6 +127,7 @@ def client_secrets(self) -> AsyncClientSecrets: @cached_property def calls(self) -> AsyncCalls: from ...lib._realtime import _AsyncCalls + return _AsyncCalls(self._client) @cached_property From ea3dcf88b10f780b71076c42bab902250e857106 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Mon, 6 Oct 2025 11:03:41 -0700 Subject: [PATCH 130/408] [fix] readd realtime and chat --- src/openai/resources/beta/beta.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index edf30be005..5ee3639db1 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -28,11 +28,24 @@ ThreadsWithStreamingResponse, AsyncThreadsWithStreamingResponse, ) +from ...resources.chat import Chat, AsyncChat +from .realtime.realtime import ( + Realtime, + AsyncRealtime, +) __all__ = ["Beta", "AsyncBeta"] class Beta(SyncAPIResource): + @cached_property + def chat(self) -> Chat: + return Chat(self._client) + + @cached_property + def realtime(self) -> Realtime: + return Realtime(self._client) + @cached_property def chatkit(self) -> ChatKit: return ChatKit(self._client) @@ -66,6 +79,14 @@ def with_streaming_response(self) -> BetaWithStreamingResponse: class AsyncBeta(AsyncAPIResource): + @cached_property + def chat(self) -> AsyncChat: + return AsyncChat(self._client) + + @cached_property + def realtime(self) -> AsyncRealtime: + return AsyncRealtime(self._client) + @cached_property def chatkit(self) -> AsyncChatKit: return AsyncChatKit(self._client) From d69edeb39e6a9cc8d9822a2838b10ab4102b4cc6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 18:04:19 +0000 Subject: [PATCH 131/408] release: 2.2.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 656a2ef17d..bfc26f9c4e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.1.0" + ".": "2.2.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 76d700f05e..336c6601a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.2.0 (2025-10-06) + +Full Changelog: [v2.1.0...v2.2.0](https://github.com/openai/openai-python/compare/v2.1.0...v2.2.0) + +### Features + +* **api:** dev day 2025 launches ([38ac009](https://github.com/openai/openai-python/commit/38ac0093ebb3419b1e2280d0dc2d26c74a2bbbec)) + + +### Bug Fixes + +* **client:** add chatkit to beta resource ([de3e561](https://github.com/openai/openai-python/commit/de3e5619d0a85b17906a9416039ef309e820dc0f)) + ## 2.1.0 (2025-10-02) Full Changelog: [v2.0.1...v2.1.0](https://github.com/openai/openai-python/compare/v2.0.1...v2.1.0) diff --git a/pyproject.toml b/pyproject.toml index d8deac4e61..7e9f9bd1ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.1.0" +version = "2.2.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 2860526ced..2fdb2912ef 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.1.0" # x-release-please-version +__version__ = "2.2.0" # x-release-please-version From 85a91ade618e7c97f382014c26aeac55a431258f Mon Sep 17 00:00:00 2001 From: giovx Date: Wed, 8 Oct 2025 11:30:46 +0200 Subject: [PATCH 132/408] chore(package): bump jiter to >=0.10.0 to support Python 3.14 (#2618) Co-authored-by: Robert Craigie --- pyproject.toml | 2 +- requirements-dev.lock | 2 +- requirements.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7e9f9bd1ee..bb5eb7cf41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ "distro>=1.7.0, <2", "sniffio", "tqdm > 4", - "jiter>=0.4.0, <1", + "jiter>=0.10.0, <1", ] requires-python = ">= 3.8" classifiers = [ diff --git a/requirements-dev.lock b/requirements-dev.lock index 0bd1c2c70f..e2446f2222 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -91,7 +91,7 @@ importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest inline-snapshot==0.28.0 -jiter==0.5.0 +jiter==0.11.0 # via openai markdown-it-py==3.0.0 # via rich diff --git a/requirements.lock b/requirements.lock index a2b6845942..aaca0834db 100644 --- a/requirements.lock +++ b/requirements.lock @@ -51,7 +51,7 @@ idna==3.4 # via anyio # via httpx # via yarl -jiter==0.6.1 +jiter==0.11.0 # via openai multidict==6.5.0 # via aiohttp From 044878859cf1257f0d0c6704e0568986e2d0686c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 01:09:07 +0000 Subject: [PATCH 133/408] feat(api): comparison filter in/not in --- .stats.yml | 6 +++--- src/openai/resources/beta/assistants.py | 12 ++++++++++++ src/openai/resources/beta/threads/runs/runs.py | 18 ++++++++++++++++++ .../resources/chat/completions/completions.py | 18 ++++++++++++++++++ src/openai/resources/files.py | 4 ++-- .../types/beta/assistant_create_params.py | 3 +++ .../types/beta/assistant_update_params.py | 3 +++ .../types/beta/threads/run_create_params.py | 3 +++ .../types/chat/completion_create_params.py | 3 +++ .../create_eval_completions_run_data_source.py | 3 +++ ...e_eval_completions_run_data_source_param.py | 3 +++ src/openai/types/evals/run_cancel_response.py | 6 ++++++ src/openai/types/evals/run_create_params.py | 6 ++++++ src/openai/types/evals/run_create_response.py | 6 ++++++ src/openai/types/evals/run_list_response.py | 6 ++++++ .../types/evals/run_retrieve_response.py | 6 ++++++ src/openai/types/graders/score_model_grader.py | 3 +++ .../types/graders/score_model_grader_param.py | 3 +++ src/openai/types/shared/comparison_filter.py | 10 +++++++--- src/openai/types/shared/reasoning.py | 3 +++ .../types/shared_params/comparison_filter.py | 10 ++++++++-- src/openai/types/shared_params/reasoning.py | 3 +++ .../types/vector_stores/vector_store_file.py | 2 +- 23 files changed, 129 insertions(+), 11 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2dc7422e7e..b5d9915ab8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-d64cf80d2ebddf175c5578f68226a3d5bbd3f7fd8d62ccac2205f3fc05a355ee.yml -openapi_spec_hash: d51e0d60d0c536f210b597a211bc5af0 -config_hash: e7c42016df9c6bd7bd6ff15101b9bc9b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-e66e85fb7f72477256dca1acb6b23396989d381c5c1b318de564195436bcb93f.yml +openapi_spec_hash: 0a4bbb5aa0ae532a072bd6b3854e70b1 +config_hash: 89bf7bb3a1f9439ffc6ea0e7dc57ba9b diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index ddac9a79cb..a958c0caa1 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -102,6 +102,9 @@ def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -309,6 +312,9 @@ def update( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -555,6 +561,9 @@ async def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -762,6 +771,9 @@ async def update( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index ec2dfa84cd..2753f5817e 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -173,6 +173,9 @@ def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -327,6 +330,9 @@ def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -477,6 +483,9 @@ def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -1603,6 +1612,9 @@ async def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -1757,6 +1769,9 @@ async def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), @@ -1907,6 +1922,9 @@ async def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), [GPT-4 Turbo](https://platform.openai.com/docs/models#gpt-4-turbo-and-gpt-4), diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 329634ba43..4b73c69ae9 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -407,6 +407,9 @@ def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -704,6 +707,9 @@ def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -992,6 +998,9 @@ def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -1845,6 +1854,9 @@ async def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -2142,6 +2154,9 @@ async def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured @@ -2430,6 +2445,9 @@ async def create( effort can result in faster responses and fewer tokens used on reasoning in a response. + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. + response_format: An object specifying the format that the model must output. Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 77bb2d613c..bb9ad43b1b 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -236,7 +236,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileDeleted: """ - Delete a file. + Delete a file and remove it from all vector stores. Args: extra_headers: Send extra headers @@ -553,7 +553,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileDeleted: """ - Delete a file. + Delete a file and remove it from all vector stores. Args: extra_headers: Send extra headers diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 07f8f28f02..6fb1551fa5 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -65,6 +65,9 @@ class AssistantCreateParams(TypedDict, total=False): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 45d9f984b2..6d20b8e01f 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -100,6 +100,9 @@ class AssistantUpdateParams(TypedDict, total=False): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index cfd272f5ad..3190c8b308 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -114,6 +114,9 @@ class RunCreateParamsBase(TypedDict, total=False): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 2ae81dfbc2..8b0fdd04b3 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -192,6 +192,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ response_format: ResponseFormat diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 74323a735e..a9f2fd0858 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -175,6 +175,9 @@ class SamplingParams(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ response_format: Optional[SamplingParamsResponseFormat] = None diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 4e9c1fdeb8..e682e2db5e 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -171,6 +171,9 @@ class SamplingParams(TypedDict, total=False): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ response_format: SamplingParamsResponseFormat diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index d04d4ff657..084dd6ce5c 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -106,6 +106,9 @@ class DataSourceResponsesSourceResponses(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ temperature: Optional[float] = None @@ -241,6 +244,9 @@ class DataSourceResponsesSamplingParams(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index 6ff897b5de..f114fae6a4 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -119,6 +119,9 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ temperature: Optional[float] @@ -259,6 +262,9 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ seed: int diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index defa275c8c..1343335e0d 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -106,6 +106,9 @@ class DataSourceResponsesSourceResponses(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ temperature: Optional[float] = None @@ -241,6 +244,9 @@ class DataSourceResponsesSamplingParams(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index 7fe0e55ace..7c32ce54a2 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -106,6 +106,9 @@ class DataSourceResponsesSourceResponses(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ temperature: Optional[float] = None @@ -241,6 +244,9 @@ class DataSourceResponsesSamplingParams(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index a50520f17d..f1212c1671 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -106,6 +106,9 @@ class DataSourceResponsesSourceResponses(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ temperature: Optional[float] = None @@ -241,6 +244,9 @@ class DataSourceResponsesSamplingParams(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index 908c6f91d3..35e2dc1468 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -70,6 +70,9 @@ class SamplingParams(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 743944e099..168feeae13 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -76,6 +76,9 @@ class SamplingParams(TypedDict, total=False): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ seed: Optional[int] diff --git a/src/openai/types/shared/comparison_filter.py b/src/openai/types/shared/comparison_filter.py index 2ec2651ff2..33415ca4f9 100644 --- a/src/openai/types/shared/comparison_filter.py +++ b/src/openai/types/shared/comparison_filter.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union +from typing import List, Union from typing_extensions import Literal from ..._models import BaseModel @@ -13,7 +13,9 @@ class ComparisonFilter(BaseModel): """The key to compare against the value.""" type: Literal["eq", "ne", "gt", "gte", "lt", "lte"] - """Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. - `eq`: equals - `ne`: not equal @@ -21,9 +23,11 @@ class ComparisonFilter(BaseModel): - `gte`: greater than or equal - `lt`: less than - `lte`: less than or equal + - `in`: in + - `nin`: not in """ - value: Union[str, float, bool] + value: Union[str, float, bool, List[Union[str, float]]] """ The value to compare against the attribute key; supports string, number, or boolean types. diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 24ce301526..2cf34eb9ae 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -17,6 +17,9 @@ class Reasoning(BaseModel): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None diff --git a/src/openai/types/shared_params/comparison_filter.py b/src/openai/types/shared_params/comparison_filter.py index 38edd315ed..1c40729c19 100644 --- a/src/openai/types/shared_params/comparison_filter.py +++ b/src/openai/types/shared_params/comparison_filter.py @@ -5,6 +5,8 @@ from typing import Union from typing_extensions import Literal, Required, TypedDict +from ..._types import SequenceNotStr + __all__ = ["ComparisonFilter"] @@ -13,7 +15,9 @@ class ComparisonFilter(TypedDict, total=False): """The key to compare against the value.""" type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte"]] - """Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`. + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. - `eq`: equals - `ne`: not equal @@ -21,9 +25,11 @@ class ComparisonFilter(TypedDict, total=False): - `gte`: greater than or equal - `lt`: less than - `lte`: less than or equal + - `in`: in + - `nin`: not in """ - value: Required[Union[str, float, bool]] + value: Required[Union[str, float, bool, SequenceNotStr[Union[str, float]]]] """ The value to compare against the attribute key; supports string, number, or boolean types. diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 7eab2c76f7..d5461a4eaa 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -18,6 +18,9 @@ class Reasoning(TypedDict, total=False): supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response. + + Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning + effort. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] diff --git a/src/openai/types/vector_stores/vector_store_file.py b/src/openai/types/vector_stores/vector_store_file.py index b59a61dfb0..001584dfd7 100644 --- a/src/openai/types/vector_stores/vector_store_file.py +++ b/src/openai/types/vector_stores/vector_store_file.py @@ -11,7 +11,7 @@ class LastError(BaseModel): code: Literal["server_error", "unsupported_file", "invalid_file"] - """One of `server_error` or `rate_limit_exceeded`.""" + """One of `server_error`, `unsupported_file`, or `invalid_file`.""" message: str """A human-readable description of the error.""" From e5f93f5daee9f3fc7646833ac235b1693f192a56 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 01:10:06 +0000 Subject: [PATCH 134/408] release: 2.3.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bfc26f9c4e..75ec52fc91 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.2.0" + ".": "2.3.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 336c6601a2..66eec00fea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.3.0 (2025-10-10) + +Full Changelog: [v2.2.0...v2.3.0](https://github.com/openai/openai-python/compare/v2.2.0...v2.3.0) + +### Features + +* **api:** comparison filter in/not in ([aa49f62](https://github.com/openai/openai-python/commit/aa49f626a6ea9d77ad008badfb3741e16232d62f)) + + +### Chores + +* **package:** bump jiter to >=0.10.0 to support Python 3.14 ([#2618](https://github.com/openai/openai-python/issues/2618)) ([aa445ca](https://github.com/openai/openai-python/commit/aa445cab5c93c6908697fe98e73e16963330b141)) + ## 2.2.0 (2025-10-06) Full Changelog: [v2.1.0...v2.2.0](https://github.com/openai/openai-python/compare/v2.1.0...v2.2.0) diff --git a/pyproject.toml b/pyproject.toml index bb5eb7cf41..7197505229 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.2.0" +version = "2.3.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 2fdb2912ef..f202a6d61c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.2.0" # x-release-please-version +__version__ = "2.3.0" # x-release-please-version From b20a9e7b813a12b22202987f512d913433b07fcf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 10 Oct 2025 08:49:48 +0000 Subject: [PATCH 135/408] chore(internal): detect missing future annotations with ruff --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7197505229..0f773e5fa4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -256,6 +256,8 @@ select = [ "B", # remove unused imports "F401", + # check for missing future annotations + "FA102", # bare except statements "E722", # unused arguments @@ -278,6 +280,8 @@ unfixable = [ "T203", ] +extend-safe-fixes = ["FA102"] + [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" From d5c64434b7b1a500e074913cd87d8a6c09f1c13e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 20:10:15 +0000 Subject: [PATCH 136/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b5d9915ab8..f6f2dd4705 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-e66e85fb7f72477256dca1acb6b23396989d381c5c1b318de564195436bcb93f.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-242fe01994cc3c6c2b1a76d8e1eaa832303fa870e4e40de4a2303ac5ce17369a.yml openapi_spec_hash: 0a4bbb5aa0ae532a072bd6b3854e70b1 -config_hash: 89bf7bb3a1f9439ffc6ea0e7dc57ba9b +config_hash: c6362759d174c1ff65e656b1cfb5efdb From 8cdfd0650ef548178939607eb39adf5df4af5b7d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:46:54 +0000 Subject: [PATCH 137/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f6f2dd4705..8bd7c486ba 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-242fe01994cc3c6c2b1a76d8e1eaa832303fa870e4e40de4a2303ac5ce17369a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-11d308a9ef78ad01aa11c880a084a3982276800d7994db3f454aa515474977d7.yml openapi_spec_hash: 0a4bbb5aa0ae532a072bd6b3854e70b1 -config_hash: c6362759d174c1ff65e656b1cfb5efdb +config_hash: f0940d0906846178759ef7128e4cb98e From 25cbb74f835206c497df2772205c7b2225951989 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:55:31 +0000 Subject: [PATCH 138/408] feat(api): Add support for gpt-4o-transcribe-diarize on audio/transcriptions endpoint --- .stats.yml | 6 +- api.md | 3 + src/openai/resources/audio/transcriptions.py | 278 +++++++++++++++--- src/openai/resources/audio/translations.py | 8 +- .../resources/vector_stores/vector_stores.py | 10 + src/openai/types/audio/__init__.py | 3 + .../audio/transcription_create_params.py | 41 ++- .../audio/transcription_create_response.py | 3 +- .../types/audio/transcription_diarized.py | 63 ++++ .../audio/transcription_diarized_segment.py | 32 ++ .../types/audio/transcription_stream_event.py | 4 +- .../audio/transcription_text_delta_event.py | 6 + .../audio/transcription_text_segment_event.py | 27 ++ src/openai/types/audio_model.py | 2 +- src/openai/types/audio_response_format.py | 2 +- .../types/realtime/audio_transcription.py | 15 +- .../realtime/audio_transcription_param.py | 11 +- .../types/vector_store_create_params.py | 6 + .../audio/test_transcriptions.py | 8 + tests/api_resources/test_vector_stores.py | 2 + tests/lib/test_audio.py | 26 +- 21 files changed, 475 insertions(+), 81 deletions(-) create mode 100644 src/openai/types/audio/transcription_diarized.py create mode 100644 src/openai/types/audio/transcription_diarized_segment.py create mode 100644 src/openai/types/audio/transcription_text_segment_event.py diff --git a/.stats.yml b/.stats.yml index 8bd7c486ba..d0ff2b0dc2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-11d308a9ef78ad01aa11c880a084a3982276800d7994db3f454aa515474977d7.yml -openapi_spec_hash: 0a4bbb5aa0ae532a072bd6b3854e70b1 -config_hash: f0940d0906846178759ef7128e4cb98e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-104cced8f4c7436a76eea02e26307828166405ccfb296faffb008b72772c11a7.yml +openapi_spec_hash: fdc03ed84a65a31b80da909255e53924 +config_hash: 03b48e9b8c7231a902403210dbd7dfa0 diff --git a/api.md b/api.md index 1c170ccdd8..7eb296318f 100644 --- a/api.md +++ b/api.md @@ -171,11 +171,14 @@ Types: ```python from openai.types.audio import ( Transcription, + TranscriptionDiarized, + TranscriptionDiarizedSegment, TranscriptionInclude, TranscriptionSegment, TranscriptionStreamEvent, TranscriptionTextDeltaEvent, TranscriptionTextDoneEvent, + TranscriptionTextSegmentEvent, TranscriptionVerbose, TranscriptionWord, TranscriptionCreateResponse, diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 1fe8866562..52e44bffb7 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -9,8 +9,17 @@ import httpx from ... import _legacy_response -from ...types import AudioResponseFormat -from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) from ..._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -23,6 +32,7 @@ from ...types.audio_response_format import AudioResponseFormat from ...types.audio.transcription_include import TranscriptionInclude from ...types.audio.transcription_verbose import TranscriptionVerbose +from ...types.audio.transcription_diarized import TranscriptionDiarized from ...types.audio.transcription_stream_event import TranscriptionStreamEvent from ...types.audio.transcription_create_response import TranscriptionCreateResponse @@ -93,6 +103,66 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranscriptionVerbose: ... + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + ) -> Transcription: ... + @overload def create( self, @@ -114,6 +184,27 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str: ... + @overload + def create( + self, + *, + file: FileTypes, + model: Union[str, AudioModel], + chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, + response_format: Literal["diarized_json"], + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, + language: str | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TranscriptionDiarized: ... + @overload def create( self, @@ -123,6 +214,8 @@ def create( stream: Literal[True], chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, language: str | Omit = omit, prompt: str | Omit = omit, response_format: Union[AudioResponseFormat, Omit] = omit, @@ -144,8 +237,8 @@ def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source - Whisper V2 model). + `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source + Whisper V2 model), and `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -160,12 +253,25 @@ def create( first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. + `gpt-4o-mini-transcribe`. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -174,11 +280,14 @@ def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, - the only supported format is `json`. + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -190,7 +299,8 @@ def create( `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps - incurs additional latency. + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. extra_headers: Send extra headers @@ -211,6 +321,8 @@ def create( stream: bool, chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, language: str | Omit = omit, prompt: str | Omit = omit, response_format: Union[AudioResponseFormat, Omit] = omit, @@ -232,8 +344,8 @@ def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source - Whisper V2 model). + `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source + Whisper V2 model), and `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -248,12 +360,25 @@ def create( first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. + `gpt-4o-mini-transcribe`. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -262,11 +387,14 @@ def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, - the only supported format is `json`. + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -278,7 +406,8 @@ def create( `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps - incurs additional latency. + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. extra_headers: Send extra headers @@ -298,6 +427,8 @@ def create( model: Union[str, AudioModel], chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, language: str | Omit = omit, prompt: str | Omit = omit, response_format: Union[AudioResponseFormat, Omit] = omit, @@ -310,13 +441,15 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> str | Transcription | TranscriptionVerbose | Stream[TranscriptionStreamEvent]: + ) -> str | Transcription | TranscriptionDiarized | TranscriptionVerbose | Stream[TranscriptionStreamEvent]: body = deepcopy_minimal( { "file": file, "model": model, "chunking_strategy": chunking_strategy, "include": include, + "known_speaker_names": known_speaker_names, + "known_speaker_references": known_speaker_references, "language": language, "prompt": prompt, "response_format": response_format, @@ -376,6 +509,8 @@ async def create( model: Union[str, AudioModel], chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, language: str | Omit = omit, prompt: str | Omit = omit, response_format: Union[Literal["json"], Omit] = omit, @@ -398,19 +533,32 @@ async def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source - Whisper V2 model). + `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source + Whisper V2 model), and `gpt-4o-transcribe-diarize`. chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. + `gpt-4o-mini-transcribe`. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -419,11 +567,14 @@ async def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, - the only supported format is `json`. + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -444,7 +595,8 @@ async def create( `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps - incurs additional latency. + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. extra_headers: Send extra headers @@ -502,6 +654,8 @@ async def create( stream: Literal[True], chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, language: str | Omit = omit, prompt: str | Omit = omit, response_format: Union[AudioResponseFormat, Omit] = omit, @@ -523,8 +677,8 @@ async def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source - Whisper V2 model). + `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source + Whisper V2 model), and `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -539,12 +693,25 @@ async def create( first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. + `gpt-4o-mini-transcribe`. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -553,11 +720,14 @@ async def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, - the only supported format is `json`. + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -569,7 +739,8 @@ async def create( `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps - incurs additional latency. + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. extra_headers: Send extra headers @@ -590,6 +761,8 @@ async def create( stream: bool, chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, language: str | Omit = omit, prompt: str | Omit = omit, response_format: Union[AudioResponseFormat, Omit] = omit, @@ -611,8 +784,8 @@ async def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source - Whisper V2 model). + `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source + Whisper V2 model), and `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -627,12 +800,25 @@ async def create( first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is transcribed as a single block. + Required when using `gpt-4o-transcribe-diarize` for inputs longer than 30 + seconds. include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. + `gpt-4o-mini-transcribe`. This field is not supported when using + `gpt-4o-transcribe-diarize`. + + known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + + known_speaker_references: Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -641,11 +827,14 @@ async def create( prompt: An optional text to guide the model's style or continue a previous audio segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, - the only supported format is `json`. + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and @@ -657,7 +846,8 @@ async def create( `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps - incurs additional latency. + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. extra_headers: Send extra headers @@ -677,6 +867,8 @@ async def create( model: Union[str, AudioModel], chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, + known_speaker_names: SequenceNotStr[str] | Omit = omit, + known_speaker_references: SequenceNotStr[str] | Omit = omit, language: str | Omit = omit, prompt: str | Omit = omit, response_format: Union[AudioResponseFormat, Omit] = omit, @@ -689,13 +881,15 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Transcription | TranscriptionVerbose | str | AsyncStream[TranscriptionStreamEvent]: + ) -> Transcription | TranscriptionVerbose | TranscriptionDiarized | str | AsyncStream[TranscriptionStreamEvent]: body = deepcopy_minimal( { "file": file, "model": model, "chunking_strategy": chunking_strategy, "include": include, + "known_speaker_names": known_speaker_names, + "known_speaker_references": known_speaker_references, "language": language, "prompt": prompt, "response_format": response_format, @@ -764,8 +958,8 @@ def __init__(self, transcriptions: AsyncTranscriptions) -> None: def _get_response_format_type( - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | Omit, -) -> type[Transcription | TranscriptionVerbose | str]: + response_format: AudioResponseFormat | Omit, +) -> type[Transcription | TranscriptionVerbose | TranscriptionDiarized | str]: if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] return Transcription @@ -773,6 +967,8 @@ def _get_response_format_type( return Transcription elif response_format == "verbose_json": return TranscriptionVerbose + elif response_format == "diarized_json": + return TranscriptionDiarized elif response_format == "srt" or response_format == "text" or response_format == "vtt": return str elif TYPE_CHECKING: # type: ignore[unreachable] diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index a4f844db13..310f901fb3 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -349,7 +349,7 @@ def __init__(self, translations: AsyncTranslations) -> None: def _get_response_format_type( - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | Omit, + response_format: AudioResponseFormat | Omit, ) -> type[Translation | TranslationVerbose | str]: if isinstance(response_format, Omit) or response_format is None: # pyright: ignore[reportUnnecessaryComparison] return Translation @@ -360,8 +360,8 @@ def _get_response_format_type( return TranslationVerbose elif response_format == "srt" or response_format == "text" or response_format == "vtt": return str - elif TYPE_CHECKING: # type: ignore[unreachable] + elif TYPE_CHECKING and response_format != "diarized_json": # type: ignore[unreachable] assert_never(response_format) else: - log.warn("Unexpected audio response format: %s", response_format) - return Transcription + log.warning("Unexpected audio response format: %s", response_format) + return Translation diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py index 39548936c8..490e3e7fdb 100644 --- a/src/openai/resources/vector_stores/vector_stores.py +++ b/src/openai/resources/vector_stores/vector_stores.py @@ -79,6 +79,7 @@ def create( self, *, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + description: str | Omit = omit, expires_after: vector_store_create_params.ExpiresAfter | Omit = omit, file_ids: SequenceNotStr[str] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, @@ -97,6 +98,9 @@ def create( chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. + description: A description for the vector store. Can be used to describe the vector store's + purpose. + expires_after: The expiration policy for a vector store. file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that @@ -126,6 +130,7 @@ def create( body=maybe_transform( { "chunking_strategy": chunking_strategy, + "description": description, "expires_after": expires_after, "file_ids": file_ids, "metadata": metadata, @@ -424,6 +429,7 @@ async def create( self, *, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + description: str | Omit = omit, expires_after: vector_store_create_params.ExpiresAfter | Omit = omit, file_ids: SequenceNotStr[str] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, @@ -442,6 +448,9 @@ async def create( chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. + description: A description for the vector store. Can be used to describe the vector store's + purpose. + expires_after: The expiration policy for a vector store. file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that @@ -471,6 +480,7 @@ async def create( body=await async_maybe_transform( { "chunking_strategy": chunking_strategy, + "description": description, "expires_after": expires_after, "file_ids": file_ids, "metadata": metadata, diff --git a/src/openai/types/audio/__init__.py b/src/openai/types/audio/__init__.py index 396944ee47..2ff2b8185d 100644 --- a/src/openai/types/audio/__init__.py +++ b/src/openai/types/audio/__init__.py @@ -11,10 +11,13 @@ from .transcription_include import TranscriptionInclude as TranscriptionInclude from .transcription_segment import TranscriptionSegment as TranscriptionSegment from .transcription_verbose import TranscriptionVerbose as TranscriptionVerbose +from .transcription_diarized import TranscriptionDiarized as TranscriptionDiarized from .translation_create_params import TranslationCreateParams as TranslationCreateParams from .transcription_stream_event import TranscriptionStreamEvent as TranscriptionStreamEvent from .transcription_create_params import TranscriptionCreateParams as TranscriptionCreateParams from .translation_create_response import TranslationCreateResponse as TranslationCreateResponse from .transcription_create_response import TranscriptionCreateResponse as TranscriptionCreateResponse from .transcription_text_done_event import TranscriptionTextDoneEvent as TranscriptionTextDoneEvent +from .transcription_diarized_segment import TranscriptionDiarizedSegment as TranscriptionDiarizedSegment from .transcription_text_delta_event import TranscriptionTextDeltaEvent as TranscriptionTextDeltaEvent +from .transcription_text_segment_event import TranscriptionTextSegmentEvent as TranscriptionTextSegmentEvent diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index f7abcced87..adaef9f5fe 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -5,7 +5,7 @@ from typing import List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ..._types import FileTypes +from ..._types import FileTypes, SequenceNotStr from ..audio_model import AudioModel from .transcription_include import TranscriptionInclude from ..audio_response_format import AudioResponseFormat @@ -29,8 +29,9 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): model: Required[Union[str, AudioModel]] """ID of the model to use. - The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` - (which is powered by our open source Whisper V2 model). + The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. """ chunking_strategy: Optional[ChunkingStrategy] @@ -39,7 +40,8 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): When set to `"auto"`, the server first normalizes loudness and then uses voice activity detection (VAD) to choose boundaries. `server_vad` object can be provided to tweak VAD detection parameters manually. If unset, the audio is - transcribed as a single block. + transcribed as a single block. Required when using `gpt-4o-transcribe-diarize` + for inputs longer than 30 seconds. """ include: List[TranscriptionInclude] @@ -48,7 +50,24 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. + `gpt-4o-mini-transcribe`. This field is not supported when using + `gpt-4o-transcribe-diarize`. + """ + + known_speaker_names: SequenceNotStr[str] + """ + Optional list of speaker names that correspond to the audio samples provided in + `known_speaker_references[]`. Each entry should be a short identifier (for + example `customer` or `agent`). Up to 4 speakers are supported. + """ + + known_speaker_references: SequenceNotStr[str] + """ + Optional list of audio samples (as + [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) + that contain known speaker references matching `known_speaker_names[]`. Each + sample must be between 2 and 10 seconds, and can use any of the same input audio + formats supported by `file`. """ language: str @@ -64,14 +83,17 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): segment. The [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. + should match the audio language. This field is not supported when using + `gpt-4o-transcribe-diarize`. """ response_format: AudioResponseFormat """ The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, - the only supported format is `json`. + `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`, the only supported format is `json`. For + `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and + `diarized_json`, with `diarized_json` required to receive speaker annotations. """ temperature: float @@ -89,7 +111,8 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps - incurs additional latency. + incurs additional latency. This option is not available for + `gpt-4o-transcribe-diarize`. """ diff --git a/src/openai/types/audio/transcription_create_response.py b/src/openai/types/audio/transcription_create_response.py index 2f7bed8114..5717a3e701 100644 --- a/src/openai/types/audio/transcription_create_response.py +++ b/src/openai/types/audio/transcription_create_response.py @@ -5,7 +5,8 @@ from .transcription import Transcription from .transcription_verbose import TranscriptionVerbose +from .transcription_diarized import TranscriptionDiarized __all__ = ["TranscriptionCreateResponse"] -TranscriptionCreateResponse: TypeAlias = Union[Transcription, TranscriptionVerbose] +TranscriptionCreateResponse: TypeAlias = Union[Transcription, TranscriptionDiarized, TranscriptionVerbose] diff --git a/src/openai/types/audio/transcription_diarized.py b/src/openai/types/audio/transcription_diarized.py new file mode 100644 index 0000000000..b7dd2b8ebb --- /dev/null +++ b/src/openai/types/audio/transcription_diarized.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .transcription_diarized_segment import TranscriptionDiarizedSegment + +__all__ = ["TranscriptionDiarized", "Usage", "UsageTokens", "UsageTokensInputTokenDetails", "UsageDuration"] + + +class UsageTokensInputTokenDetails(BaseModel): + audio_tokens: Optional[int] = None + """Number of audio tokens billed for this request.""" + + text_tokens: Optional[int] = None + """Number of text tokens billed for this request.""" + + +class UsageTokens(BaseModel): + input_tokens: int + """Number of input tokens billed for this request.""" + + output_tokens: int + """Number of output tokens generated.""" + + total_tokens: int + """Total number of tokens used (input + output).""" + + type: Literal["tokens"] + """The type of the usage object. Always `tokens` for this variant.""" + + input_token_details: Optional[UsageTokensInputTokenDetails] = None + """Details about the input tokens billed for this request.""" + + +class UsageDuration(BaseModel): + seconds: float + """Duration of the input audio in seconds.""" + + type: Literal["duration"] + """The type of the usage object. Always `duration` for this variant.""" + + +Usage: TypeAlias = Annotated[Union[UsageTokens, UsageDuration], PropertyInfo(discriminator="type")] + + +class TranscriptionDiarized(BaseModel): + duration: float + """Duration of the input audio in seconds.""" + + segments: List[TranscriptionDiarizedSegment] + """Segments of the transcript annotated with timestamps and speaker labels.""" + + task: Literal["transcribe"] + """The type of task that was run. Always `transcribe`.""" + + text: str + """The concatenated transcript text for the entire audio input.""" + + usage: Optional[Usage] = None + """Token or duration usage statistics for the request.""" diff --git a/src/openai/types/audio/transcription_diarized_segment.py b/src/openai/types/audio/transcription_diarized_segment.py new file mode 100644 index 0000000000..fe87bb4fb8 --- /dev/null +++ b/src/openai/types/audio/transcription_diarized_segment.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TranscriptionDiarizedSegment"] + + +class TranscriptionDiarizedSegment(BaseModel): + id: str + """Unique identifier for the segment.""" + + end: float + """End timestamp of the segment in seconds.""" + + speaker: str + """Speaker label for this segment. + + When known speakers are provided, the label matches `known_speaker_names[]`. + Otherwise speakers are labeled sequentially using capital letters (`A`, `B`, + ...). + """ + + start: float + """Start timestamp of the segment in seconds.""" + + text: str + """Transcript text for this segment.""" + + type: Literal["transcript.text.segment"] + """The type of the segment. Always `transcript.text.segment`.""" diff --git a/src/openai/types/audio/transcription_stream_event.py b/src/openai/types/audio/transcription_stream_event.py index 757077a280..77d3a3aeec 100644 --- a/src/openai/types/audio/transcription_stream_event.py +++ b/src/openai/types/audio/transcription_stream_event.py @@ -6,9 +6,11 @@ from ..._utils import PropertyInfo from .transcription_text_done_event import TranscriptionTextDoneEvent from .transcription_text_delta_event import TranscriptionTextDeltaEvent +from .transcription_text_segment_event import TranscriptionTextSegmentEvent __all__ = ["TranscriptionStreamEvent"] TranscriptionStreamEvent: TypeAlias = Annotated[ - Union[TranscriptionTextDeltaEvent, TranscriptionTextDoneEvent], PropertyInfo(discriminator="type") + Union[TranscriptionTextSegmentEvent, TranscriptionTextDeltaEvent, TranscriptionTextDoneEvent], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/audio/transcription_text_delta_event.py b/src/openai/types/audio/transcription_text_delta_event.py index 36c52f0623..363b6a6335 100644 --- a/src/openai/types/audio/transcription_text_delta_event.py +++ b/src/openai/types/audio/transcription_text_delta_event.py @@ -33,3 +33,9 @@ class TranscriptionTextDeltaEvent(BaseModel): [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `include[]` parameter set to `logprobs`. """ + + segment_id: Optional[str] = None + """Identifier of the diarized segment that this delta belongs to. + + Only present when using `gpt-4o-transcribe-diarize`. + """ diff --git a/src/openai/types/audio/transcription_text_segment_event.py b/src/openai/types/audio/transcription_text_segment_event.py new file mode 100644 index 0000000000..d4f7664578 --- /dev/null +++ b/src/openai/types/audio/transcription_text_segment_event.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TranscriptionTextSegmentEvent"] + + +class TranscriptionTextSegmentEvent(BaseModel): + id: str + """Unique identifier for the segment.""" + + end: float + """End timestamp of the segment in seconds.""" + + speaker: str + """Speaker label for this segment.""" + + start: float + """Start timestamp of the segment in seconds.""" + + text: str + """Transcript text for this segment.""" + + type: Literal["transcript.text.segment"] + """The type of the event. Always `transcript.text.segment`.""" diff --git a/src/openai/types/audio_model.py b/src/openai/types/audio_model.py index 4d14d60181..68031a2198 100644 --- a/src/openai/types/audio_model.py +++ b/src/openai/types/audio_model.py @@ -4,4 +4,4 @@ __all__ = ["AudioModel"] -AudioModel: TypeAlias = Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe"] +AudioModel: TypeAlias = Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe", "gpt-4o-transcribe-diarize"] diff --git a/src/openai/types/audio_response_format.py b/src/openai/types/audio_response_format.py index f8c8d45945..1897aaf6ed 100644 --- a/src/openai/types/audio_response_format.py +++ b/src/openai/types/audio_response_format.py @@ -4,4 +4,4 @@ __all__ = ["AudioResponseFormat"] -AudioResponseFormat: TypeAlias = Literal["json", "text", "srt", "verbose_json", "vtt"] +AudioResponseFormat: TypeAlias = Literal["json", "text", "srt", "verbose_json", "vtt", "diarized_json"] diff --git a/src/openai/types/realtime/audio_transcription.py b/src/openai/types/realtime/audio_transcription.py index cf662b3aa2..3e5c8e0cb4 100644 --- a/src/openai/types/realtime/audio_transcription.py +++ b/src/openai/types/realtime/audio_transcription.py @@ -17,13 +17,14 @@ class AudioTranscription(BaseModel): format will improve accuracy and latency. """ - model: Optional[Literal["whisper-1", "gpt-4o-transcribe-latest", "gpt-4o-mini-transcribe", "gpt-4o-transcribe"]] = ( - None - ) + model: Optional[ + Literal["whisper-1", "gpt-4o-mini-transcribe", "gpt-4o-transcribe", "gpt-4o-transcribe-diarize"] + ] = None """The model to use for transcription. - Current options are `whisper-1`, `gpt-4o-transcribe-latest`, - `gpt-4o-mini-transcribe`, and `gpt-4o-transcribe`. + Current options are `whisper-1`, `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, + and `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need + diarization with speaker labels. """ prompt: Optional[str] = None @@ -31,6 +32,6 @@ class AudioTranscription(BaseModel): An optional text to guide the model's style or continue a previous audio segment. For `whisper-1`, the [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". + For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the + prompt is a free text string, for example "expect words related to technology". """ diff --git a/src/openai/types/realtime/audio_transcription_param.py b/src/openai/types/realtime/audio_transcription_param.py index fb09f105b8..3b65e42c8f 100644 --- a/src/openai/types/realtime/audio_transcription_param.py +++ b/src/openai/types/realtime/audio_transcription_param.py @@ -16,11 +16,12 @@ class AudioTranscriptionParam(TypedDict, total=False): format will improve accuracy and latency. """ - model: Literal["whisper-1", "gpt-4o-transcribe-latest", "gpt-4o-mini-transcribe", "gpt-4o-transcribe"] + model: Literal["whisper-1", "gpt-4o-mini-transcribe", "gpt-4o-transcribe", "gpt-4o-transcribe-diarize"] """The model to use for transcription. - Current options are `whisper-1`, `gpt-4o-transcribe-latest`, - `gpt-4o-mini-transcribe`, and `gpt-4o-transcribe`. + Current options are `whisper-1`, `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, + and `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need + diarization with speaker labels. """ prompt: str @@ -28,6 +29,6 @@ class AudioTranscriptionParam(TypedDict, total=False): An optional text to guide the model's style or continue a previous audio segment. For `whisper-1`, the [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). - For `gpt-4o-transcribe` models, the prompt is a free text string, for example - "expect words related to technology". + For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the + prompt is a free text string, for example "expect words related to technology". """ diff --git a/src/openai/types/vector_store_create_params.py b/src/openai/types/vector_store_create_params.py index 945a9886a3..f373a6ed28 100644 --- a/src/openai/types/vector_store_create_params.py +++ b/src/openai/types/vector_store_create_params.py @@ -20,6 +20,12 @@ class VectorStoreCreateParams(TypedDict, total=False): non-empty. """ + description: str + """A description for the vector store. + + Can be used to describe the vector store's purpose. + """ + expires_after: ExpiresAfter """The expiration policy for a vector store.""" diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index 11cbe2349c..b5eaa4be1f 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -32,6 +32,8 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: model="gpt-4o-transcribe", chunking_strategy="auto", include=["logprobs"], + known_speaker_names=["string"], + known_speaker_references=["string"], language="language", prompt="prompt", response_format="json", @@ -84,6 +86,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: stream=True, chunking_strategy="auto", include=["logprobs"], + known_speaker_names=["string"], + known_speaker_references=["string"], language="language", prompt="prompt", response_format="json", @@ -140,6 +144,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn model="gpt-4o-transcribe", chunking_strategy="auto", include=["logprobs"], + known_speaker_names=["string"], + known_speaker_references=["string"], language="language", prompt="prompt", response_format="json", @@ -192,6 +198,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn stream=True, chunking_strategy="auto", include=["logprobs"], + known_speaker_names=["string"], + known_speaker_references=["string"], language="language", prompt="prompt", response_format="json", diff --git a/tests/api_resources/test_vector_stores.py b/tests/api_resources/test_vector_stores.py index dffd2b1d07..cce9c52cea 100644 --- a/tests/api_resources/test_vector_stores.py +++ b/tests/api_resources/test_vector_stores.py @@ -31,6 +31,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: vector_store = client.vector_stores.create( chunking_strategy={"type": "auto"}, + description="description", expires_after={ "anchor": "last_active_at", "days": 1, @@ -299,6 +300,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: vector_store = await async_client.vector_stores.create( chunking_strategy={"type": "auto"}, + description="description", expires_after={ "anchor": "last_active_at", "days": 1, diff --git a/tests/lib/test_audio.py b/tests/lib/test_audio.py index ff8dba4714..93ed3a33b2 100644 --- a/tests/lib/test_audio.py +++ b/tests/lib/test_audio.py @@ -44,7 +44,8 @@ def test_translation_create_overloads_in_sync(sync: bool, client: OpenAI, async_ elif is_literal_type(typ): overload_response_formats.update(get_args(typ)) - src_response_formats: set[str] = set(get_args(AudioResponseFormat)) + # 'diarized_json' applies only to transcriptions, not translations. + src_response_formats: set[str] = set(get_args(AudioResponseFormat)) - {"diarized_json"} diff = src_response_formats.difference(overload_response_formats) assert len(diff) == 0, f"some response format options don't have overloads" @@ -57,18 +58,27 @@ def test_transcription_create_overloads_in_sync(sync: bool, client: OpenAI, asyn overload_response_formats: set[str] = set() for i, overload in enumerate(typing_extensions.get_overloads(fn)): - assert_signatures_in_sync( - fn, - overload, - exclude_params={"response_format", "stream"}, - description=f" for overload {i}", - ) - sig = inspect.signature(overload) typ = evaluate_forwardref( sig.parameters["response_format"].annotation, globalns=sys.modules[fn.__module__].__dict__, ) + + exclude_params = {"response_format", "stream"} + # known_speaker_names and known_speaker_references are only supported by diarized_json + if not (is_literal_type(typ) and set(get_args(typ)) == {"diarized_json"}): + exclude_params.update({"known_speaker_names", "known_speaker_references"}) + + # diarized_json does not support these parameters + if is_literal_type(typ) and set(get_args(typ)) == {"diarized_json"}: + exclude_params.update({"include", "prompt", "timestamp_granularities"}) + + assert_signatures_in_sync( + fn, + overload, + exclude_params=exclude_params, + description=f" for overload {i}", + ) if is_union_type(typ): for arg in get_args(typ): if not is_literal_type(arg): From e043d7b164c9ee9b34f7029606f08ed60d2d47db Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 16 Oct 2025 11:08:09 -0400 Subject: [PATCH 139/408] chore: fix dangling comment --- src/openai/resources/audio/transcriptions.py | 60 -------------------- 1 file changed, 60 deletions(-) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 52e44bffb7..30ef39deec 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -103,66 +103,6 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TranscriptionVerbose: ... - model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. This field is not supported when using - `gpt-4o-transcribe-diarize`. - - known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in - `known_speaker_references[]`. Each entry should be a short identifier (for - example `customer` or `agent`). Up to 4 speakers are supported. - - known_speaker_references: Optional list of audio samples (as - [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)) - that contain known speaker references matching `known_speaker_names[]`. Each - sample must be between 2 and 10 seconds, and can use any of the same input audio - formats supported by `file`. - - language: The language of the input audio. Supplying the input language in - [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) - format will improve accuracy and latency. - - prompt: An optional text to guide the model's style or continue a previous audio - segment. The - [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) - should match the audio language. This field is not supported when using - `gpt-4o-transcribe-diarize`. - - response_format: The format of the output, in one of these options: `json`, `text`, `srt`, - `verbose_json`, `vtt`, or `diarized_json`. For `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`, the only supported format is `json`. For - `gpt-4o-transcribe-diarize`, the supported formats are `json`, `text`, and - `diarized_json`, with `diarized_json` required to receive speaker annotations. - - stream: If set to true, the model response data will be streamed to the client as it is - generated using - [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). - See the - [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) - for more information. - - Note: Streaming is not supported for the `whisper-1` model and will be ignored. - - temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the - output more random, while lower values like 0.2 will make it more focused and - deterministic. If set to 0, the model will use - [log probability](https://en.wikipedia.org/wiki/Log_probability) to - automatically increase the temperature until certain thresholds are hit. - - timestamp_granularities: The timestamp granularities to populate for this transcription. - `response_format` must be set `verbose_json` to use timestamp granularities. - Either or both of these options are supported: `word`, or `segment`. Note: There - is no additional latency for segment timestamps, but generating word timestamps - incurs additional latency. This option is not available for - `gpt-4o-transcribe-diarize`. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - ) -> Transcription: ... - @overload def create( self, From ebf32212f7bf5bec6b24cc2276ac0d9a28dd63bb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:08:49 +0000 Subject: [PATCH 140/408] release: 2.4.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 75ec52fc91..b44b287037 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.3.0" + ".": "2.4.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 66eec00fea..30f898c23b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 2.4.0 (2025-10-16) + +Full Changelog: [v2.3.0...v2.4.0](https://github.com/openai/openai-python/compare/v2.3.0...v2.4.0) + +### Features + +* **api:** Add support for gpt-4o-transcribe-diarize on audio/transcriptions endpoint ([bdbe9b8](https://github.com/openai/openai-python/commit/bdbe9b8f440209afa2979db4a9eda9579b3d2550)) + + +### Chores + +* fix dangling comment ([da14e99](https://github.com/openai/openai-python/commit/da14e9960608f7ade6f5cdf91967830c8a6c1657)) +* **internal:** detect missing future annotations with ruff ([2672b8f](https://github.com/openai/openai-python/commit/2672b8f0726300f7c62c356f25545ef0b3c0bb2e)) + ## 2.3.0 (2025-10-10) Full Changelog: [v2.2.0...v2.3.0](https://github.com/openai/openai-python/compare/v2.2.0...v2.3.0) diff --git a/pyproject.toml b/pyproject.toml index 0f773e5fa4..43de9882f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.3.0" +version = "2.4.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index f202a6d61c..e09654e09d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.3.0" # x-release-please-version +__version__ = "2.4.0" # x-release-please-version From 513ae76253cccdf9d0a9122b32cf0adb8d26f792 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:13:47 -0400 Subject: [PATCH 141/408] release: 2.5.0 (#2694) * chore: bump `httpx-aiohttp` version to 0.1.9 * feat(api): api update * codegen metadata * codegen metadata * release: 2.5.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 8 +- CHANGELOG.md | 13 ++ pyproject.toml | 4 +- requirements-dev.lock | 2 +- requirements.lock | 2 +- src/openai/_version.py | 2 +- src/openai/resources/beta/chatkit/chatkit.py | 125 ------------------ src/openai/types/beta/__init__.py | 4 - .../types/beta/chatkit_upload_file_params.py | 17 --- .../beta/chatkit_upload_file_response.py | 12 -- src/openai/types/beta/file_part.py | 28 ---- src/openai/types/beta/image_part.py | 31 ----- tests/api_resources/beta/test_chatkit.py | 86 ------------ 14 files changed, 23 insertions(+), 313 deletions(-) delete mode 100644 src/openai/types/beta/chatkit_upload_file_params.py delete mode 100644 src/openai/types/beta/chatkit_upload_file_response.py delete mode 100644 src/openai/types/beta/file_part.py delete mode 100644 src/openai/types/beta/image_part.py delete mode 100644 tests/api_resources/beta/test_chatkit.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b44b287037..4dedeaebc3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.4.0" + ".": "2.5.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d0ff2b0dc2..b15622eba0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-104cced8f4c7436a76eea02e26307828166405ccfb296faffb008b72772c11a7.yml -openapi_spec_hash: fdc03ed84a65a31b80da909255e53924 -config_hash: 03b48e9b8c7231a902403210dbd7dfa0 +configured_endpoints: 135 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f59befea071ed7729cbb7bce219e7f837eccfdb57e01698514e6a0bd6052ff60.yml +openapi_spec_hash: 49da48619d37932b2e257c532078b2bb +config_hash: 1af83449a09a3b4f276444dbcdd3eb67 diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f898c23b..3f79fb75fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.5.0 (2025-10-17) + +Full Changelog: [v2.4.0...v2.5.0](https://github.com/openai/openai-python/compare/v2.4.0...v2.5.0) + +### Features + +* **api:** api update ([8b280d5](https://github.com/openai/openai-python/commit/8b280d57d6d361bc3a032e030158f6859c445291)) + + +### Chores + +* bump `httpx-aiohttp` version to 0.1.9 ([67f2f0a](https://github.com/openai/openai-python/commit/67f2f0afe51dab9d5899fe18b1a4e86b2c774d10)) + ## 2.4.0 (2025-10-16) Full Changelog: [v2.3.0...v2.4.0](https://github.com/openai/openai-python/compare/v2.3.0...v2.4.0) diff --git a/pyproject.toml b/pyproject.toml index 43de9882f2..a2f0ab7176 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.4.0" +version = "2.5.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" @@ -44,7 +44,7 @@ Repository = "https://github.com/openai/openai-python" openai = "openai.cli:main" [project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] realtime = ["websockets >= 13, < 16"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] diff --git a/requirements-dev.lock b/requirements-dev.lock index e2446f2222..b454537b96 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -79,7 +79,7 @@ httpx==0.28.1 # via httpx-aiohttp # via openai # via respx -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via openai idna==3.4 # via anyio diff --git a/requirements.lock b/requirements.lock index aaca0834db..b047cb3f88 100644 --- a/requirements.lock +++ b/requirements.lock @@ -45,7 +45,7 @@ httpcore==1.0.9 httpx==0.28.1 # via httpx-aiohttp # via openai -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via openai idna==3.4 # via anyio diff --git a/src/openai/_version.py b/src/openai/_version.py index e09654e09d..0d92388b98 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.4.0" # x-release-please-version +__version__ = "2.5.0" # x-release-please-version diff --git a/src/openai/resources/beta/chatkit/chatkit.py b/src/openai/resources/beta/chatkit/chatkit.py index 2d090cf396..5a10a39c7b 100644 --- a/src/openai/resources/beta/chatkit/chatkit.py +++ b/src/openai/resources/beta/chatkit/chatkit.py @@ -2,11 +2,6 @@ from __future__ import annotations -from typing import Any, Mapping, cast - -import httpx - -from .... import _legacy_response from .threads import ( Threads, AsyncThreads, @@ -23,14 +18,8 @@ SessionsWithStreamingResponse, AsyncSessionsWithStreamingResponse, ) -from ...._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....types.beta import chatkit_upload_file_params -from ...._base_client import make_request_options -from ....types.beta.chatkit_upload_file_response import ChatKitUploadFileResponse __all__ = ["ChatKit", "AsyncChatKit"] @@ -63,55 +52,6 @@ def with_streaming_response(self) -> ChatKitWithStreamingResponse: """ return ChatKitWithStreamingResponse(self) - def upload_file( - self, - *, - file: FileTypes, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ChatKitUploadFileResponse: - """ - Upload a ChatKit file - - Args: - file: Binary file contents to store with the ChatKit session. Supports PDFs and PNG, - JPG, JPEG, GIF, or WEBP images. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} - body = deepcopy_minimal({"file": file}) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return cast( - ChatKitUploadFileResponse, - self._post( - "/chatkit/files", - body=maybe_transform(body, chatkit_upload_file_params.ChatKitUploadFileParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, ChatKitUploadFileResponse - ), # Union types cannot be passed in as arguments in the type system - ), - ) - class AsyncChatKit(AsyncAPIResource): @cached_property @@ -141,64 +81,11 @@ def with_streaming_response(self) -> AsyncChatKitWithStreamingResponse: """ return AsyncChatKitWithStreamingResponse(self) - async def upload_file( - self, - *, - file: FileTypes, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ChatKitUploadFileResponse: - """ - Upload a ChatKit file - - Args: - file: Binary file contents to store with the ChatKit session. Supports PDFs and PNG, - JPG, JPEG, GIF, or WEBP images. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} - body = deepcopy_minimal({"file": file}) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return cast( - ChatKitUploadFileResponse, - await self._post( - "/chatkit/files", - body=await async_maybe_transform(body, chatkit_upload_file_params.ChatKitUploadFileParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, ChatKitUploadFileResponse - ), # Union types cannot be passed in as arguments in the type system - ), - ) - class ChatKitWithRawResponse: def __init__(self, chatkit: ChatKit) -> None: self._chatkit = chatkit - self.upload_file = _legacy_response.to_raw_response_wrapper( - chatkit.upload_file, - ) - @cached_property def sessions(self) -> SessionsWithRawResponse: return SessionsWithRawResponse(self._chatkit.sessions) @@ -212,10 +99,6 @@ class AsyncChatKitWithRawResponse: def __init__(self, chatkit: AsyncChatKit) -> None: self._chatkit = chatkit - self.upload_file = _legacy_response.async_to_raw_response_wrapper( - chatkit.upload_file, - ) - @cached_property def sessions(self) -> AsyncSessionsWithRawResponse: return AsyncSessionsWithRawResponse(self._chatkit.sessions) @@ -229,10 +112,6 @@ class ChatKitWithStreamingResponse: def __init__(self, chatkit: ChatKit) -> None: self._chatkit = chatkit - self.upload_file = to_streamed_response_wrapper( - chatkit.upload_file, - ) - @cached_property def sessions(self) -> SessionsWithStreamingResponse: return SessionsWithStreamingResponse(self._chatkit.sessions) @@ -246,10 +125,6 @@ class AsyncChatKitWithStreamingResponse: def __init__(self, chatkit: AsyncChatKit) -> None: self._chatkit = chatkit - self.upload_file = async_to_streamed_response_wrapper( - chatkit.upload_file, - ) - @cached_property def sessions(self) -> AsyncSessionsWithStreamingResponse: return AsyncSessionsWithStreamingResponse(self._chatkit.sessions) diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index 9ef6283864..deb2369677 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -4,8 +4,6 @@ from .thread import Thread as Thread from .assistant import Assistant as Assistant -from .file_part import FilePart as FilePart -from .image_part import ImagePart as ImagePart from .function_tool import FunctionTool as FunctionTool from .assistant_tool import AssistantTool as AssistantTool from .thread_deleted import ThreadDeleted as ThreadDeleted @@ -23,11 +21,9 @@ from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams -from .chatkit_upload_file_params import ChatKitUploadFileParams as ChatKitUploadFileParams from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption -from .chatkit_upload_file_response import ChatKitUploadFileResponse as ChatKitUploadFileResponse from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption diff --git a/src/openai/types/beta/chatkit_upload_file_params.py b/src/openai/types/beta/chatkit_upload_file_params.py deleted file mode 100644 index 87dc993664..0000000000 --- a/src/openai/types/beta/chatkit_upload_file_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from ..._types import FileTypes - -__all__ = ["ChatKitUploadFileParams"] - - -class ChatKitUploadFileParams(TypedDict, total=False): - file: Required[FileTypes] - """Binary file contents to store with the ChatKit session. - - Supports PDFs and PNG, JPG, JPEG, GIF, or WEBP images. - """ diff --git a/src/openai/types/beta/chatkit_upload_file_response.py b/src/openai/types/beta/chatkit_upload_file_response.py deleted file mode 100644 index 9527df76fb..0000000000 --- a/src/openai/types/beta/chatkit_upload_file_response.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .file_part import FilePart -from .image_part import ImagePart - -__all__ = ["ChatKitUploadFileResponse"] - -ChatKitUploadFileResponse: TypeAlias = Annotated[Union[FilePart, ImagePart], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/beta/file_part.py b/src/openai/types/beta/file_part.py deleted file mode 100644 index cf60bddc99..0000000000 --- a/src/openai/types/beta/file_part.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["FilePart"] - - -class FilePart(BaseModel): - id: str - """Unique identifier for the uploaded file.""" - - mime_type: Optional[str] = None - """MIME type reported for the uploaded file. Defaults to null when unknown.""" - - name: Optional[str] = None - """Original filename supplied by the uploader. Defaults to null when unnamed.""" - - type: Literal["file"] - """Type discriminator that is always `file`.""" - - upload_url: Optional[str] = None - """Signed URL for downloading the uploaded file. - - Defaults to null when no download link is available. - """ diff --git a/src/openai/types/beta/image_part.py b/src/openai/types/beta/image_part.py deleted file mode 100644 index 4c06b4730b..0000000000 --- a/src/openai/types/beta/image_part.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ImagePart"] - - -class ImagePart(BaseModel): - id: str - """Unique identifier for the uploaded image.""" - - mime_type: str - """MIME type of the uploaded image.""" - - name: Optional[str] = None - """Original filename for the uploaded image. Defaults to null when unnamed.""" - - preview_url: str - """Preview URL that can be rendered inline for the image.""" - - type: Literal["image"] - """Type discriminator that is always `image`.""" - - upload_url: Optional[str] = None - """Signed URL for downloading the uploaded image. - - Defaults to null when no download link is available. - """ diff --git a/tests/api_resources/beta/test_chatkit.py b/tests/api_resources/beta/test_chatkit.py deleted file mode 100644 index b05be0ece5..0000000000 --- a/tests/api_resources/beta/test_chatkit.py +++ /dev/null @@ -1,86 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from openai import OpenAI, AsyncOpenAI -from tests.utils import assert_matches_type -from openai.types.beta import ChatKitUploadFileResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestChatKit: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_upload_file(self, client: OpenAI) -> None: - chatkit = client.beta.chatkit.upload_file( - file=b"raw file contents", - ) - assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) - - @parametrize - def test_raw_response_upload_file(self, client: OpenAI) -> None: - response = client.beta.chatkit.with_raw_response.upload_file( - file=b"raw file contents", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chatkit = response.parse() - assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) - - @parametrize - def test_streaming_response_upload_file(self, client: OpenAI) -> None: - with client.beta.chatkit.with_streaming_response.upload_file( - file=b"raw file contents", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chatkit = response.parse() - assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncChatKit: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_upload_file(self, async_client: AsyncOpenAI) -> None: - chatkit = await async_client.beta.chatkit.upload_file( - file=b"raw file contents", - ) - assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) - - @parametrize - async def test_raw_response_upload_file(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.chatkit.with_raw_response.upload_file( - file=b"raw file contents", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - chatkit = response.parse() - assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) - - @parametrize - async def test_streaming_response_upload_file(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.chatkit.with_streaming_response.upload_file( - file=b"raw file contents", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - chatkit = await response.parse() - assert_matches_type(ChatKitUploadFileResponse, chatkit, path=["response"]) - - assert cast(Any, response.is_closed) is True From 0a5ad3e89de194c7142a24857c86e6b852e4e2e8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 21:31:29 +0000 Subject: [PATCH 142/408] fix(api): internal openapi updates --- .stats.yml | 6 ++-- src/openai/resources/images.py | 30 ++++--------------- .../response_code_interpreter_tool_call.py | 4 +-- ...sponse_code_interpreter_tool_call_param.py | 4 +-- .../responses/response_computer_tool_call.py | 11 +++---- ...response_computer_tool_call_output_item.py | 4 +-- .../response_computer_tool_call_param.py | 11 +++---- .../types/responses/response_includable.py | 8 +++-- .../api_resources/conversations/test_items.py | 12 ++++---- .../responses/test_input_items.py | 4 +-- tests/api_resources/test_responses.py | 16 +++++----- 11 files changed, 44 insertions(+), 66 deletions(-) diff --git a/.stats.yml b/.stats.yml index b15622eba0..9cef71c261 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 135 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f59befea071ed7729cbb7bce219e7f837eccfdb57e01698514e6a0bd6052ff60.yml -openapi_spec_hash: 49da48619d37932b2e257c532078b2bb -config_hash: 1af83449a09a3b4f276444dbcdd3eb67 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b062c33330de7e3bbf992fd4f0799afd868c30a66c39418dd2c62f4add3b45b6.yml +openapi_spec_hash: fe067f5b1c0e93799b5ea7fde3c4b1b3 +config_hash: 4b6f471b24d659514b86b736c90a0c0a diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 265be6f743..9bb332230f 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -168,10 +168,7 @@ def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, - especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -285,10 +282,7 @@ def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, - especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -398,10 +392,7 @@ def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, - especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1055,10 +1046,7 @@ async def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, - especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1172,10 +1160,7 @@ async def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, - especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1285,10 +1270,7 @@ async def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, - especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, diff --git a/src/openai/types/responses/response_code_interpreter_tool_call.py b/src/openai/types/responses/response_code_interpreter_tool_call.py index ed720ecd42..b651581520 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call.py @@ -14,12 +14,12 @@ class OutputLogs(BaseModel): """The logs output from the code interpreter.""" type: Literal["logs"] - """The type of the output. Always 'logs'.""" + """The type of the output. Always `logs`.""" class OutputImage(BaseModel): type: Literal["image"] - """The type of the output. Always 'image'.""" + """The type of the output. Always `image`.""" url: str """The URL of the image output from the code interpreter.""" diff --git a/src/openai/types/responses/response_code_interpreter_tool_call_param.py b/src/openai/types/responses/response_code_interpreter_tool_call_param.py index 78b90ca87e..d402b872a4 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call_param.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call_param.py @@ -13,12 +13,12 @@ class OutputLogs(TypedDict, total=False): """The logs output from the code interpreter.""" type: Required[Literal["logs"]] - """The type of the output. Always 'logs'.""" + """The type of the output. Always `logs`.""" class OutputImage(TypedDict, total=False): type: Required[Literal["image"]] - """The type of the output. Always 'image'.""" + """The type of the output. Always `image`.""" url: Required[str] """The URL of the image output from the code interpreter.""" diff --git a/src/openai/types/responses/response_computer_tool_call.py b/src/openai/types/responses/response_computer_tool_call.py index 994837567a..f1476fa0fb 100644 --- a/src/openai/types/responses/response_computer_tool_call.py +++ b/src/openai/types/responses/response_computer_tool_call.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union +from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -31,10 +31,7 @@ class ActionClick(BaseModel): """ type: Literal["click"] - """Specifies the event type. - - For a click action, this property is always set to `click`. - """ + """Specifies the event type. For a click action, this property is always `click`.""" x: int """The x-coordinate where the click occurred.""" @@ -181,10 +178,10 @@ class PendingSafetyCheck(BaseModel): id: str """The ID of the pending safety check.""" - code: str + code: Optional[str] = None """The type of the pending safety check.""" - message: str + message: Optional[str] = None """Details about the pending safety check.""" diff --git a/src/openai/types/responses/response_computer_tool_call_output_item.py b/src/openai/types/responses/response_computer_tool_call_output_item.py index a2dd68f579..e1ac358cc6 100644 --- a/src/openai/types/responses/response_computer_tool_call_output_item.py +++ b/src/openai/types/responses/response_computer_tool_call_output_item.py @@ -13,10 +13,10 @@ class AcknowledgedSafetyCheck(BaseModel): id: str """The ID of the pending safety check.""" - code: str + code: Optional[str] = None """The type of the pending safety check.""" - message: str + message: Optional[str] = None """Details about the pending safety check.""" diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py index 0be63db2fe..228f76bac9 100644 --- a/src/openai/types/responses/response_computer_tool_call_param.py +++ b/src/openai/types/responses/response_computer_tool_call_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr @@ -32,10 +32,7 @@ class ActionClick(TypedDict, total=False): """ type: Required[Literal["click"]] - """Specifies the event type. - - For a click action, this property is always set to `click`. - """ + """Specifies the event type. For a click action, this property is always `click`.""" x: Required[int] """The x-coordinate where the click occurred.""" @@ -179,10 +176,10 @@ class PendingSafetyCheck(TypedDict, total=False): id: Required[str] """The ID of the pending safety check.""" - code: Required[str] + code: Optional[str] """The type of the pending safety check.""" - message: Required[str] + message: Optional[str] """Details about the pending safety check.""" diff --git a/src/openai/types/responses/response_includable.py b/src/openai/types/responses/response_includable.py index c17a02560f..675c83405a 100644 --- a/src/openai/types/responses/response_includable.py +++ b/src/openai/types/responses/response_includable.py @@ -5,10 +5,12 @@ __all__ = ["ResponseIncludable"] ResponseIncludable: TypeAlias = Literal[ - "code_interpreter_call.outputs", - "computer_call_output.output.image_url", "file_search_call.results", + "web_search_call.results", + "web_search_call.action.sources", "message.input_image.image_url", - "message.output_text.logprobs", + "computer_call_output.output.image_url", + "code_interpreter_call.outputs", "reasoning.encrypted_content", + "message.output_text.logprobs", ] diff --git a/tests/api_resources/conversations/test_items.py b/tests/api_resources/conversations/test_items.py index 0df88dc199..0503301f16 100644 --- a/tests/api_resources/conversations/test_items.py +++ b/tests/api_resources/conversations/test_items.py @@ -47,7 +47,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "type": "message", } ], - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], ) assert_matches_type(ConversationItemList, item, path=["response"]) @@ -116,7 +116,7 @@ def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: item = client.conversations.items.retrieve( item_id="msg_abc", conversation_id="conv_123", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], ) assert_matches_type(ConversationItem, item, path=["response"]) @@ -172,7 +172,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: item = client.conversations.items.list( conversation_id="conv_123", after="after", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], limit=0, order="asc", ) @@ -288,7 +288,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "type": "message", } ], - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], ) assert_matches_type(ConversationItemList, item, path=["response"]) @@ -357,7 +357,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) item = await async_client.conversations.items.retrieve( item_id="msg_abc", conversation_id="conv_123", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], ) assert_matches_type(ConversationItem, item, path=["response"]) @@ -413,7 +413,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N item = await async_client.conversations.items.list( conversation_id="conv_123", after="after", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], limit=0, order="asc", ) diff --git a/tests/api_resources/responses/test_input_items.py b/tests/api_resources/responses/test_input_items.py index eda20c9a0b..ed6fddf33a 100644 --- a/tests/api_resources/responses/test_input_items.py +++ b/tests/api_resources/responses/test_input_items.py @@ -30,7 +30,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: input_item = client.responses.input_items.list( response_id="response_id", after="after", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], limit=0, order="asc", ) @@ -85,7 +85,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N input_item = await async_client.responses.input_items.list( response_id="response_id", after="after", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], limit=0, order="asc", ) diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 0cc20e926b..a329aa4d9e 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -30,7 +30,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: response = client.responses.create( background=True, conversation="string", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], input="string", instructions="instructions", max_output_tokens=0, @@ -110,7 +110,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: stream=True, background=True, conversation="string", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], input="string", instructions="instructions", max_output_tokens=0, @@ -190,7 +190,7 @@ def test_method_retrieve_overload_1(self, client: OpenAI) -> None: def test_method_retrieve_with_all_params_overload_1(self, client: OpenAI) -> None: response = client.responses.retrieve( response_id="resp_677efb5139a88190b512bc3fef8e535d", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], include_obfuscation=True, starting_after=0, stream=False, @@ -241,7 +241,7 @@ def test_method_retrieve_with_all_params_overload_2(self, client: OpenAI) -> Non response_stream = client.responses.retrieve( response_id="resp_677efb5139a88190b512bc3fef8e535d", stream=True, - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], include_obfuscation=True, starting_after=0, ) @@ -383,7 +383,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn response = await async_client.responses.create( background=True, conversation="string", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], input="string", instructions="instructions", max_output_tokens=0, @@ -463,7 +463,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn stream=True, background=True, conversation="string", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], input="string", instructions="instructions", max_output_tokens=0, @@ -543,7 +543,7 @@ async def test_method_retrieve_overload_1(self, async_client: AsyncOpenAI) -> No async def test_method_retrieve_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.retrieve( response_id="resp_677efb5139a88190b512bc3fef8e535d", - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], include_obfuscation=True, starting_after=0, stream=False, @@ -594,7 +594,7 @@ async def test_method_retrieve_with_all_params_overload_2(self, async_client: As response_stream = await async_client.responses.retrieve( response_id="resp_677efb5139a88190b512bc3fef8e535d", stream=True, - include=["code_interpreter_call.outputs"], + include=["file_search_call.results"], include_obfuscation=True, starting_after=0, ) From 62a184d7634588fe3d8b9900071b079db6db92ac Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:44:45 +0000 Subject: [PATCH 143/408] feat(api): Add responses.input_tokens.count --- .stats.yml | 8 +- api.md | 12 + examples/responses_input_tokens.py | 54 +++ src/openai/resources/responses/__init__.py | 14 + .../resources/responses/input_tokens.py | 309 ++++++++++++++++++ src/openai/resources/responses/responses.py | 32 ++ src/openai/types/responses/__init__.py | 2 + .../responses/input_token_count_params.py | 138 ++++++++ .../responses/input_token_count_response.py | 13 + .../responses/test_input_tokens.py | 138 ++++++++ 10 files changed, 716 insertions(+), 4 deletions(-) create mode 100644 examples/responses_input_tokens.py create mode 100644 src/openai/resources/responses/input_tokens.py create mode 100644 src/openai/types/responses/input_token_count_params.py create mode 100644 src/openai/types/responses/input_token_count_response.py create mode 100644 tests/api_resources/responses/test_input_tokens.py diff --git a/.stats.yml b/.stats.yml index 9cef71c261..48649a27d0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 135 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b062c33330de7e3bbf992fd4f0799afd868c30a66c39418dd2c62f4add3b45b6.yml -openapi_spec_hash: fe067f5b1c0e93799b5ea7fde3c4b1b3 -config_hash: 4b6f471b24d659514b86b736c90a0c0a +configured_endpoints: 136 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-812a10f8fb54c584efc914422b574cb3f43dc238b5733b13f6a0b2308b7d9910.yml +openapi_spec_hash: 0222041ba12a5ff6b94924a834fa91a2 +config_hash: 50ee3382a63c021a9f821a935950e926 diff --git a/api.md b/api.md index 7eb296318f..96642c01ad 100644 --- a/api.md +++ b/api.md @@ -865,6 +865,18 @@ Methods: - client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] +## InputTokens + +Types: + +```python +from openai.types.responses import InputTokenCountResponse +``` + +Methods: + +- client.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse + # Realtime Types: diff --git a/examples/responses_input_tokens.py b/examples/responses_input_tokens.py new file mode 100644 index 0000000000..39809b928f --- /dev/null +++ b/examples/responses_input_tokens.py @@ -0,0 +1,54 @@ +from typing import List + +from openai import OpenAI +from openai.types.responses.tool_param import ToolParam +from openai.types.responses.response_input_item_param import ResponseInputItemParam + + +def main() -> None: + client = OpenAI() + tools: List[ToolParam] = [ + { + "type": "function", + "name": "get_current_weather", + "description": "Get current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "City and state, e.g. San Francisco, CA", + }, + "unit": { + "type": "string", + "enum": ["c", "f"], + "description": "Temperature unit to use", + }, + }, + "required": ["location", "unit"], + "additionalProperties": False, + }, + "strict": True, + } + ] + + input_items: List[ResponseInputItemParam] = [ + { + "type": "message", + "role": "user", + "content": [{"type": "input_text", "text": "What's the weather in San Francisco today?"}], + } + ] + + response = client.responses.input_tokens.count( + model="gpt-5", + instructions="You are a concise assistant.", + input=input_items, + tools=tools, + tool_choice={"type": "function", "name": "get_current_weather"}, + ) + print(f"input tokens: {response.input_tokens}") + + +if __name__ == "__main__": + main() diff --git a/src/openai/resources/responses/__init__.py b/src/openai/resources/responses/__init__.py index ad19218b01..51d318ad8d 100644 --- a/src/openai/resources/responses/__init__.py +++ b/src/openai/resources/responses/__init__.py @@ -16,6 +16,14 @@ InputItemsWithStreamingResponse, AsyncInputItemsWithStreamingResponse, ) +from .input_tokens import ( + InputTokens, + AsyncInputTokens, + InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse, +) __all__ = [ "InputItems", @@ -24,6 +32,12 @@ "AsyncInputItemsWithRawResponse", "InputItemsWithStreamingResponse", "AsyncInputItemsWithStreamingResponse", + "InputTokens", + "AsyncInputTokens", + "InputTokensWithRawResponse", + "AsyncInputTokensWithRawResponse", + "InputTokensWithStreamingResponse", + "AsyncInputTokensWithStreamingResponse", "Responses", "AsyncResponses", "ResponsesWithRawResponse", diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py new file mode 100644 index 0000000000..0f47955fe4 --- /dev/null +++ b/src/openai/resources/responses/input_tokens.py @@ -0,0 +1,309 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal + +import httpx + +from ... import _legacy_response +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ..._base_client import make_request_options +from ...types.responses import input_token_count_params +from ...types.responses.tool_param import ToolParam +from ...types.shared_params.reasoning import Reasoning +from ...types.responses.response_input_item_param import ResponseInputItemParam +from ...types.responses.input_token_count_response import InputTokenCountResponse + +__all__ = ["InputTokens", "AsyncInputTokens"] + + +class InputTokens(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputTokensWithStreamingResponse(self) + + def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[ToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Get input token counts + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/responses/input_tokens", + body=maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InputTokenCountResponse, + ) + + +class AsyncInputTokens(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputTokensWithStreamingResponse(self) + + async def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[ToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Get input token counts + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/responses/input_tokens", + body=await async_maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=InputTokenCountResponse, + ) + + +class InputTokensWithRawResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.to_raw_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithRawResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.async_to_raw_response_wrapper( + input_tokens.count, + ) + + +class InputTokensWithStreamingResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = to_streamed_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithStreamingResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = async_to_streamed_response_wrapper( + input_tokens.count, + ) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 0a89d0c18e..439cf8d3ad 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -24,6 +24,14 @@ ) from ..._streaming import Stream, AsyncStream from ...lib._tools import PydanticFunctionTool, ResponsesPydanticFunctionTool +from .input_tokens import ( + InputTokens, + AsyncInputTokens, + InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse, +) from ..._base_client import make_request_options from ...types.responses import response_create_params, response_retrieve_params from ...lib._parsing._responses import ( @@ -52,6 +60,10 @@ class Responses(SyncAPIResource): def input_items(self) -> InputItems: return InputItems(self._client) + @cached_property + def input_tokens(self) -> InputTokens: + return InputTokens(self._client) + @cached_property def with_raw_response(self) -> ResponsesWithRawResponse: """ @@ -1483,6 +1495,10 @@ class AsyncResponses(AsyncAPIResource): def input_items(self) -> AsyncInputItems: return AsyncInputItems(self._client) + @cached_property + def input_tokens(self) -> AsyncInputTokens: + return AsyncInputTokens(self._client) + @cached_property def with_raw_response(self) -> AsyncResponsesWithRawResponse: """ @@ -2938,6 +2954,10 @@ def __init__(self, responses: Responses) -> None: def input_items(self) -> InputItemsWithRawResponse: return InputItemsWithRawResponse(self._responses.input_items) + @cached_property + def input_tokens(self) -> InputTokensWithRawResponse: + return InputTokensWithRawResponse(self._responses.input_tokens) + class AsyncResponsesWithRawResponse: def __init__(self, responses: AsyncResponses) -> None: @@ -2963,6 +2983,10 @@ def __init__(self, responses: AsyncResponses) -> None: def input_items(self) -> AsyncInputItemsWithRawResponse: return AsyncInputItemsWithRawResponse(self._responses.input_items) + @cached_property + def input_tokens(self) -> AsyncInputTokensWithRawResponse: + return AsyncInputTokensWithRawResponse(self._responses.input_tokens) + class ResponsesWithStreamingResponse: def __init__(self, responses: Responses) -> None: @@ -2985,6 +3009,10 @@ def __init__(self, responses: Responses) -> None: def input_items(self) -> InputItemsWithStreamingResponse: return InputItemsWithStreamingResponse(self._responses.input_items) + @cached_property + def input_tokens(self) -> InputTokensWithStreamingResponse: + return InputTokensWithStreamingResponse(self._responses.input_tokens) + class AsyncResponsesWithStreamingResponse: def __init__(self, responses: AsyncResponses) -> None: @@ -3007,6 +3035,10 @@ def __init__(self, responses: AsyncResponses) -> None: def input_items(self) -> AsyncInputItemsWithStreamingResponse: return AsyncInputItemsWithStreamingResponse(self._responses.input_items) + @cached_property + def input_tokens(self) -> AsyncInputTokensWithStreamingResponse: + return AsyncInputTokensWithStreamingResponse(self._responses.input_tokens) + def _make_tools(tools: Iterable[ParseableToolParam] | Omit) -> List[ToolParam] | Omit: if not is_given(tools): diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 458118741b..fd70836e50 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -62,6 +62,7 @@ from .tool_choice_types_param import ToolChoiceTypesParam as ToolChoiceTypesParam from .web_search_preview_tool import WebSearchPreviewTool as WebSearchPreviewTool from .easy_input_message_param import EasyInputMessageParam as EasyInputMessageParam +from .input_token_count_params import InputTokenCountParams as InputTokenCountParams from .response_completed_event import ResponseCompletedEvent as ResponseCompletedEvent from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent @@ -74,6 +75,7 @@ from .response_input_text_param import ResponseInputTextParam as ResponseInputTextParam from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent from .tool_choice_allowed_param import ToolChoiceAllowedParam as ToolChoiceAllowedParam +from .input_token_count_response import InputTokenCountResponse as InputTokenCountResponse from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent from .response_input_audio_param import ResponseInputAudioParam as ResponseInputAudioParam diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py new file mode 100644 index 0000000000..d442a2d171 --- /dev/null +++ b/src/openai/types/responses/input_token_count_params.py @@ -0,0 +1,138 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, TypeAlias, TypedDict + +from .tool_param import ToolParam +from .tool_choice_options import ToolChoiceOptions +from .tool_choice_mcp_param import ToolChoiceMcpParam +from .tool_choice_types_param import ToolChoiceTypesParam +from ..shared_params.reasoning import Reasoning +from .tool_choice_custom_param import ToolChoiceCustomParam +from .response_input_item_param import ResponseInputItemParam +from .tool_choice_allowed_param import ToolChoiceAllowedParam +from .tool_choice_function_param import ToolChoiceFunctionParam +from .response_conversation_param import ResponseConversationParam +from .response_format_text_config_param import ResponseFormatTextConfigParam + +__all__ = ["InputTokenCountParams", "Conversation", "Text", "ToolChoice"] + + +class InputTokenCountParams(TypedDict, total=False): + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + input: Union[str, Iterable[ResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + model: Optional[str] + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + reasoning: Optional[Reasoning] + """ + **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + text: Optional[Text] + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ToolChoice] + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Optional[Iterable[ToolParam]] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + """ + + truncation: Literal["auto", "disabled"] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. - `disabled` (default): If the + input size will exceed the context window size for a model, the request will + fail with a 400 error. + """ + + +Conversation: TypeAlias = Union[str, ResponseConversationParam] + + +class Text(TypedDict, total=False): + format: ResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ + + +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowedParam, + ToolChoiceTypesParam, + ToolChoiceFunctionParam, + ToolChoiceMcpParam, + ToolChoiceCustomParam, +] diff --git a/src/openai/types/responses/input_token_count_response.py b/src/openai/types/responses/input_token_count_response.py new file mode 100644 index 0000000000..30ddfc1217 --- /dev/null +++ b/src/openai/types/responses/input_token_count_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputTokenCountResponse"] + + +class InputTokenCountResponse(BaseModel): + input_tokens: int + + object: Literal["response.input_tokens"] diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py new file mode 100644 index 0000000000..54ba2d25c2 --- /dev/null +++ b/tests/api_resources/responses/test_input_tokens.py @@ -0,0 +1,138 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.responses import InputTokenCountResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInputTokens: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_count(self, client: OpenAI) -> None: + input_token = client.responses.input_tokens.count() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + def test_method_count_with_all_params(self, client: OpenAI) -> None: + input_token = client.responses.input_tokens.count( + conversation="string", + input="string", + instructions="instructions", + model="model", + parallel_tool_calls=True, + previous_response_id="resp_123", + reasoning={ + "effort": "minimal", + "generate_summary": "auto", + "summary": "auto", + }, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "description": "description", + } + ], + truncation="auto", + ) + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + def test_raw_response_count(self, client: OpenAI) -> None: + response = client.responses.input_tokens.with_raw_response.count() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_token = response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + def test_streaming_response_count(self, client: OpenAI) -> None: + with client.responses.input_tokens.with_streaming_response.count() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_token = response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncInputTokens: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_count(self, async_client: AsyncOpenAI) -> None: + input_token = await async_client.responses.input_tokens.count() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> None: + input_token = await async_client.responses.input_tokens.count( + conversation="string", + input="string", + instructions="instructions", + model="model", + parallel_tool_calls=True, + previous_response_id="resp_123", + reasoning={ + "effort": "minimal", + "generate_summary": "auto", + "summary": "auto", + }, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "description": "description", + } + ], + truncation="auto", + ) + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + async def test_raw_response_count(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.input_tokens.with_raw_response.count() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_token = response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + async def test_streaming_response_count(self, async_client: AsyncOpenAI) -> None: + async with async_client.responses.input_tokens.with_streaming_response.count() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_token = await response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + assert cast(Any, response.is_closed) is True From dff16b5e9964bf85157eb41181255ed5dde8dda4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:45:52 +0000 Subject: [PATCH 144/408] release: 2.6.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4dedeaebc3..511dd5165c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.5.0" + ".": "2.6.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f79fb75fe..1ac76520f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.6.0 (2025-10-20) + +Full Changelog: [v2.5.0...v2.6.0](https://github.com/openai/openai-python/compare/v2.5.0...v2.6.0) + +### Features + +* **api:** Add responses.input_tokens.count ([6dd09e2](https://github.com/openai/openai-python/commit/6dd09e2829f385f72b28620888d91a4493c96772)) + + +### Bug Fixes + +* **api:** internal openapi updates ([caabd7c](https://github.com/openai/openai-python/commit/caabd7c81f0f557f66dc0089af460185a5816c11)) + ## 2.5.0 (2025-10-17) Full Changelog: [v2.4.0...v2.5.0](https://github.com/openai/openai-python/compare/v2.4.0...v2.5.0) diff --git a/pyproject.toml b/pyproject.toml index a2f0ab7176..bcf990f3c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.5.0" +version = "2.6.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0d92388b98..7183b0287b 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.5.0" # x-release-please-version +__version__ = "2.6.0" # x-release-please-version From e01f14b2acacfb8d74a308c7878632833a44561e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 19:43:07 +0000 Subject: [PATCH 145/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 48649a27d0..acef7ba4ff 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-812a10f8fb54c584efc914422b574cb3f43dc238b5733b13f6a0b2308b7d9910.yml -openapi_spec_hash: 0222041ba12a5ff6b94924a834fa91a2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a4bb37d110a22c2888f53e21281434686a6fffa3e672a40f2503ad9bd2759063.yml +openapi_spec_hash: 2d59eefb494dff4eea8c3d008c7e2070 config_hash: 50ee3382a63c021a9f821a935950e926 From 8dcfe8b259868c7d5b5384cdc7bfe1d51e320f68 Mon Sep 17 00:00:00 2001 From: David Meadows Date: Fri, 17 Oct 2025 14:18:38 -0400 Subject: [PATCH 146/408] copy over translations non-streaming + json def from async --- src/openai/resources/audio/transcriptions.py | 66 +++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 30ef39deec..056e15eca6 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -69,9 +69,10 @@ def create( model: Union[str, AudioModel], chunking_strategy: Optional[transcription_create_params.ChunkingStrategy] | Omit = omit, include: List[TranscriptionInclude] | Omit = omit, - response_format: Union[Literal["json"], Omit] = omit, language: str | Omit = omit, prompt: str | Omit = omit, + response_format: Union[Literal["json"], Omit] = omit, + stream: Optional[Literal[False]] | Omit = omit, temperature: float | Omit = omit, timestamp_granularities: List[Literal["word", "segment"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -80,7 +81,68 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Transcription: ... + ) -> TranscriptionCreateResponse: + """ + Transcribes audio into the input language. + + Args: + file: + The audio file object (not file name) to transcribe, in one of these formats: + flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. + + model: ID of the model to use. The options are `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source + Whisper V2 model). + + chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server + first normalizes loudness and then uses voice activity detection (VAD) to choose + boundaries. `server_vad` object can be provided to tweak VAD detection + parameters manually. If unset, the audio is transcribed as a single block. + + include: Additional information to include in the transcription response. `logprobs` will + return the log probabilities of the tokens in the response to understand the + model's confidence in the transcription. `logprobs` only works with + response_format set to `json` and only with the models `gpt-4o-transcribe` and + `gpt-4o-mini-transcribe`. + + language: The language of the input audio. Supplying the input language in + [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) + format will improve accuracy and latency. + + prompt: An optional text to guide the model's style or continue a previous audio + segment. The + [prompt](https://platform.openai.com/docs/guides/speech-to-text#prompting) + should match the audio language. + + response_format: The format of the output, in one of these options: `json`, `text`, `srt`, + `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, + the only supported format is `json`. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section of the Speech-to-Text guide](https://platform.openai.com/docs/guides/speech-to-text?lang=curl#streaming-transcriptions) + for more information. + + Note: Streaming is not supported for the `whisper-1` model and will be ignored. + + temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the + output more random, while lower values like 0.2 will make it more focused and + deterministic. If set to 0, the model will use + [log probability](https://en.wikipedia.org/wiki/Log_probability) to + automatically increase the temperature until certain thresholds are hit. + + timestamp_granularities: The timestamp granularities to populate for this transcription. + `response_format` must be set `verbose_json` to use timestamp granularities. + Either or both of these options are supported: `word`, or `segment`. Note: There + is no additional latency for segment timestamps, but generating word timestamps + incurs additional latency. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + """ @overload def create( From f05eec91fe6dcfefb9ccadfeb6cf2154d77714fe Mon Sep 17 00:00:00 2001 From: David Meadows Date: Fri, 17 Oct 2025 14:33:26 -0400 Subject: [PATCH 147/408] revert change to response type --- src/openai/resources/audio/transcriptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 056e15eca6..a5c86146d4 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -81,7 +81,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TranscriptionCreateResponse: + ) -> Transcription: """ Transcribes audio into the input language. From 663e89602cb5957e9dd584514a9b06f0d3f5385a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 22:33:48 +0000 Subject: [PATCH 148/408] fix(api): docs updates --- .stats.yml | 4 +- src/openai/resources/files.py | 54 ++++++++++----------- src/openai/types/shared/reasoning.py | 2 + src/openai/types/shared_params/reasoning.py | 2 + 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/.stats.yml b/.stats.yml index acef7ba4ff..b4309cd4c3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a4bb37d110a22c2888f53e21281434686a6fffa3e672a40f2503ad9bd2759063.yml -openapi_spec_hash: 2d59eefb494dff4eea8c3d008c7e2070 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a3c45d9bd3bb25bf4eaa49b7fb473a00038293dec659ffaa44f624ded884abf4.yml +openapi_spec_hash: 9c20aaf786a0700dabd13d9865481c9e config_hash: 50ee3382a63c021a9f821a935950e926 diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index bb9ad43b1b..cc117e7f15 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -71,20 +71,19 @@ def create( up to 512 MB, and the size of all files uploaded by one organization can be up to 1 TB. - The Assistants API supports files up to 2 million tokens and of specific file - types. See the - [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for - details. - - The Fine-tuning API only supports `.jsonl` files. The input also has certain - required formats for fine-tuning - [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or - [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) - models. - - The Batch API only supports `.jsonl` files up to 200 MB in size. The input also - has a specific required - [format](https://platform.openai.com/docs/api-reference/batch/request-input). + - The Assistants API supports files up to 2 million tokens and of specific file + types. See the + [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) + for details. + - The Fine-tuning API only supports `.jsonl` files. The input also has certain + required formats for fine-tuning + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) + or + [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + models. + - The Batch API only supports `.jsonl` files up to 200 MB in size. The input + also has a specific required + [format](https://platform.openai.com/docs/api-reference/batch/request-input). Please [contact us](https://help.openai.com/) if you need to increase these storage limits. @@ -388,20 +387,19 @@ async def create( up to 512 MB, and the size of all files uploaded by one organization can be up to 1 TB. - The Assistants API supports files up to 2 million tokens and of specific file - types. See the - [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for - details. - - The Fine-tuning API only supports `.jsonl` files. The input also has certain - required formats for fine-tuning - [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or - [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) - models. - - The Batch API only supports `.jsonl` files up to 200 MB in size. The input also - has a specific required - [format](https://platform.openai.com/docs/api-reference/batch/request-input). + - The Assistants API supports files up to 2 million tokens and of specific file + types. See the + [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) + for details. + - The Fine-tuning API only supports `.jsonl` files. The input also has certain + required formats for fine-tuning + [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) + or + [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input) + models. + - The Batch API only supports `.jsonl` files up to 200 MB in size. The input + also has a specific required + [format](https://platform.openai.com/docs/api-reference/batch/request-input). Please [contact us](https://help.openai.com/) if you need to increase these storage limits. diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 2cf34eb9ae..6ea2fe82bf 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -35,4 +35,6 @@ class Reasoning(BaseModel): This can be useful for debugging and understanding the model's reasoning process. One of `auto`, `concise`, or `detailed`. + + `concise` is only supported for `computer-use-preview` models. """ diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index d5461a4eaa..5c1eff683f 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -36,4 +36,6 @@ class Reasoning(TypedDict, total=False): This can be useful for debugging and understanding the model's reasoning process. One of `auto`, `concise`, or `detailed`. + + `concise` is only supported for `computer-use-preview` models. """ From 4e8856576211064b09c0cc4a1ed35b82b169abe2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 05:04:02 +0000 Subject: [PATCH 149/408] release: 2.6.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 511dd5165c..2f8909f197 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.6.0" + ".": "2.6.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ac76520f3..0ce541566d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.6.1 (2025-10-24) + +Full Changelog: [v2.6.0...v2.6.1](https://github.com/openai/openai-python/compare/v2.6.0...v2.6.1) + +### Bug Fixes + +* **api:** docs updates ([d01a0c9](https://github.com/openai/openai-python/commit/d01a0c96ecb94c78b7e16546790c573704b7515b)) + + +### Chores + +* **client:** clean up custom translations code ([cfb9e25](https://github.com/openai/openai-python/commit/cfb9e25855b8eb020abe02cdd99566adf474e821)) + ## 2.6.0 (2025-10-20) Full Changelog: [v2.5.0...v2.6.0](https://github.com/openai/openai-python/compare/v2.5.0...v2.6.0) diff --git a/pyproject.toml b/pyproject.toml index bcf990f3c0..e96101b51c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.6.0" +version = "2.6.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7183b0287b..b0fe817996 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.6.0" # x-release-please-version +__version__ = "2.6.1" # x-release-please-version From b16bf4eb46cce0a2414086fd14f2e6a7497e8754 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 00:54:12 +0000 Subject: [PATCH 150/408] feat(api): remove InputAudio from ResponseInputContent Removes the type `InputAudio` from `ResponseInputContent`. This parameter was non-functional and has now been removed. Please note that this is not a feature removal; it was never supported by the Responses API. While this is technically a backward-incompatible change due to the type removal, it reflects the intended behavior and has no functional impact. --- .stats.yml | 4 ++-- src/openai/types/responses/response_input_content.py | 4 +--- src/openai/types/responses/response_input_content_param.py | 5 +---- .../responses/response_input_message_content_list_param.py | 5 +---- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/.stats.yml b/.stats.yml index b4309cd4c3..bc4e084f99 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a3c45d9bd3bb25bf4eaa49b7fb473a00038293dec659ffaa44f624ded884abf4.yml -openapi_spec_hash: 9c20aaf786a0700dabd13d9865481c9e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f68f718cd45ac3f9336603601bccc38a718af44d0b26601031de3d0a71b7ce2f.yml +openapi_spec_hash: 1560717860bba4105936647dde8f618d config_hash: 50ee3382a63c021a9f821a935950e926 diff --git a/src/openai/types/responses/response_input_content.py b/src/openai/types/responses/response_input_content.py index 376b9ffce8..1726909a17 100644 --- a/src/openai/types/responses/response_input_content.py +++ b/src/openai/types/responses/response_input_content.py @@ -6,12 +6,10 @@ from ..._utils import PropertyInfo from .response_input_file import ResponseInputFile from .response_input_text import ResponseInputText -from .response_input_audio import ResponseInputAudio from .response_input_image import ResponseInputImage __all__ = ["ResponseInputContent"] ResponseInputContent: TypeAlias = Annotated[ - Union[ResponseInputText, ResponseInputImage, ResponseInputFile, ResponseInputAudio], - PropertyInfo(discriminator="type"), + Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") ] diff --git a/src/openai/types/responses/response_input_content_param.py b/src/openai/types/responses/response_input_content_param.py index a95e026a53..7791cdfd8e 100644 --- a/src/openai/types/responses/response_input_content_param.py +++ b/src/openai/types/responses/response_input_content_param.py @@ -7,11 +7,8 @@ from .response_input_file_param import ResponseInputFileParam from .response_input_text_param import ResponseInputTextParam -from .response_input_audio_param import ResponseInputAudioParam from .response_input_image_param import ResponseInputImageParam __all__ = ["ResponseInputContentParam"] -ResponseInputContentParam: TypeAlias = Union[ - ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam, ResponseInputAudioParam -] +ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] diff --git a/src/openai/types/responses/response_input_message_content_list_param.py b/src/openai/types/responses/response_input_message_content_list_param.py index 8e3778d15a..080613df0d 100644 --- a/src/openai/types/responses/response_input_message_content_list_param.py +++ b/src/openai/types/responses/response_input_message_content_list_param.py @@ -7,13 +7,10 @@ from .response_input_file_param import ResponseInputFileParam from .response_input_text_param import ResponseInputTextParam -from .response_input_audio_param import ResponseInputAudioParam from .response_input_image_param import ResponseInputImageParam __all__ = ["ResponseInputMessageContentListParam", "ResponseInputContentParam"] -ResponseInputContentParam: TypeAlias = Union[ - ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam, ResponseInputAudioParam -] +ResponseInputContentParam: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] ResponseInputMessageContentListParam: TypeAlias = List[ResponseInputContentParam] From 6132922c6d5d67708f3e6f1d09ddc85cff7ad32c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 10:42:58 +0000 Subject: [PATCH 151/408] fix(client): close streams without requiring full consumption --- src/openai/_streaming.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index f586de74ff..05c284a2be 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -96,9 +96,8 @@ def __stream__(self) -> Iterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) - # Ensure the entire stream is consumed - for _sse in iterator: - ... + # As we might not fully consume the response stream, we need to close it explicitly + response.close() def __enter__(self) -> Self: return self @@ -198,9 +197,8 @@ async def __stream__(self) -> AsyncIterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) - # Ensure the entire stream is consumed - async for _sse in iterator: - ... + # As we might not fully consume the response stream, we need to close it explicitly + await response.aclose() async def __aenter__(self) -> Self: return self From 35cb4e9bb6df5f9b746d818e80f7bafe72b30dfd Mon Sep 17 00:00:00 2001 From: Dan Martins Date: Wed, 29 Oct 2025 08:53:08 -0400 Subject: [PATCH 152/408] fix(readme): update realtime examples (#2714) --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9311b477a3..21f79312a1 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,9 @@ async def main(): client = AsyncOpenAI() async with client.realtime.connect(model="gpt-realtime") as connection: - await connection.session.update(session={'modalities': ['text']}) + await connection.session.update( + session={"type": "realtime", "output_modalities": ["text"]} + ) await connection.conversation.item.create( item={ @@ -256,10 +258,10 @@ async def main(): await connection.response.create() async for event in connection: - if event.type == 'response.text.delta': + if event.type == "response.output_text.delta": print(event.delta, flush=True, end="") - elif event.type == 'response.text.done': + elif event.type == "response.output_text.done": print() elif event.type == "response.done": From 214d13284e95e3223dcc0fd954c8e9c93db5b89a Mon Sep 17 00:00:00 2001 From: Showmick Das Date: Thu, 30 Oct 2025 05:52:29 -0400 Subject: [PATCH 153/408] fix(uploads): avoid file handle leak --- src/openai/resources/uploads/uploads.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 8953256f2a..e8c047bd4f 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -157,9 +157,8 @@ def upload_file_chunked( part = self.parts.create(upload_id=upload.id, data=data) log.info("Uploaded part %s for upload %s", part.id, upload.id) part_ids.append(part.id) - except Exception: + finally: buf.close() - raise return self.complete(upload_id=upload.id, part_ids=part_ids, md5=md5) @@ -465,9 +464,8 @@ async def upload_file_chunked( part = await self.parts.create(upload_id=upload.id, data=data) log.info("Uploaded part %s for upload %s", part.id, upload.id) part_ids.append(part.id) - except Exception: + finally: buf.close() - raise return await self.complete(upload_id=upload.id, part_ids=part_ids, md5=md5) From 5327844316860adc1ca534e6001d163d0e23fd79 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 11:05:12 +0000 Subject: [PATCH 154/408] chore(internal/tests): avoid race condition with implicit client cleanup --- tests/test_client.py | 372 +++++++++++++++++++++++-------------------- 1 file changed, 202 insertions(+), 170 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 3287e0e706..e8d62f17f7 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -64,51 +64,49 @@ def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int: class TestOpenAI: - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - def test_raw_response(self, respx_mock: MockRouter) -> None: + def test_raw_response(self, respx_mock: MockRouter, client: OpenAI) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: OpenAI) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, client: OpenAI) -> None: + copied = client.copy() + assert id(copied) != id(client) - copied = self.client.copy(api_key="another My API Key") + copied = client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, client: OpenAI) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = OpenAI( @@ -143,6 +141,7 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() def test_copy_default_query(self) -> None: client = OpenAI( @@ -180,13 +179,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + client.close() + + def test_copy_signature(self, client: OpenAI) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -197,12 +198,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, client: OpenAI) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -259,14 +260,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + def test_request_timeout(self, client: OpenAI) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( - FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) - ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) @@ -277,6 +276,8 @@ def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + client.close() + def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: @@ -288,6 +289,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + client.close() + # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = OpenAI( @@ -298,6 +301,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + client.close() + # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = OpenAI( @@ -308,6 +313,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + client.close() + async def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): async with httpx.AsyncClient() as http_client: @@ -319,14 +326,14 @@ async def test_invalid_http_client(self) -> None: ) def test_default_headers_option(self) -> None: - client = OpenAI( + test_client = OpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = OpenAI( + test_client2 = OpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, @@ -335,10 +342,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + test_client.close() + test_client2.close() + def test_validate_headers(self) -> None: client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) options = client._prepare_options(FinalRequestOptions(method="get", url="/foo")) @@ -369,8 +379,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + client.close() + + def test_request_extra_json(self, client: OpenAI) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -381,7 +393,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -392,7 +404,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -403,8 +415,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: OpenAI) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -414,7 +426,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -425,8 +437,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: OpenAI) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -439,7 +451,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -453,7 +465,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -496,7 +508,7 @@ def test_multipart_repeating_array(self, client: OpenAI) -> None: ] @pytest.mark.respx(base_url=base_url) - def test_basic_union_response(self, respx_mock: MockRouter) -> None: + def test_basic_union_response(self, respx_mock: MockRouter, client: OpenAI) -> None: class Model1(BaseModel): name: str @@ -505,12 +517,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + def test_union_response_different_types(self, respx_mock: MockRouter, client: OpenAI) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -521,18 +533,18 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: OpenAI) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -548,7 +560,7 @@ class Model(BaseModel): ) ) - response = self.client.get("/foo", cast_to=Model) + response = client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 @@ -560,6 +572,8 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" + client.close() + def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): client = OpenAI(api_key=api_key, _strict_response_validation=True) @@ -587,6 +601,7 @@ def test_base_url_trailing_slash(self, client: OpenAI) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -610,6 +625,7 @@ def test_base_url_no_trailing_slash(self, client: OpenAI) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -633,35 +649,36 @@ def test_absolute_request_url(self, client: OpenAI) -> None: ), ) assert request.url == "https://myapi.com/foo" + client.close() def test_copied_client_does_not_close_http(self) -> None: - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied - assert not client.is_closed() + assert not test_client.is_closed() def test_client_context_manager(self) -> None: - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - with client as c2: - assert c2 is client + test_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + def test_client_response_validation_error(self, respx_mock: MockRouter, client: OpenAI) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - self.client.get("/foo", cast_to=Model) + client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -670,13 +687,13 @@ def test_client_max_retries_validation(self) -> None: OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) @pytest.mark.respx(base_url=base_url) - def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + def test_default_stream_cls(self, respx_mock: MockRouter, client: OpenAI) -> None: class Model(BaseModel): name: str respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - stream = self.client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) + stream = client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) assert isinstance(stream, Stream) stream.response.close() @@ -692,11 +709,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = client.get("/foo", cast_to=Model) + response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + strict_client.close() + non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -719,9 +739,9 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: OpenAI + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) @@ -743,7 +763,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien model="gpt-4o", ).__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -760,7 +780,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client ], model="gpt-4o", ).__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -919,28 +939,26 @@ def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - def test_follow_redirects(self, respx_mock: MockRouter) -> None: + def test_follow_redirects(self, respx_mock: MockRouter, client: OpenAI) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: OpenAI) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - self.client.post( - "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response - ) + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" @@ -1003,55 +1021,51 @@ def test_copy_auth(self) -> None: class TestAsyncOpenAI: - client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response(self, respx_mock: MockRouter) -> None: + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, async_client: AsyncOpenAI) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) - copied = self.client.copy(api_key="another My API Key") + copied = async_client.copy(api_key="another My API Key") assert copied.api_key == "another My API Key" - assert self.client.api_key == "My API Key" + assert async_client.api_key == "My API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, async_client: AsyncOpenAI) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = async_client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert async_client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(async_client.timeout, httpx.Timeout) - def test_copy_default_headers(self) -> None: + async def test_copy_default_headers(self) -> None: client = AsyncOpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) @@ -1084,8 +1098,9 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() - def test_copy_default_query(self) -> None: + async def test_copy_default_query(self) -> None: client = AsyncOpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) @@ -1121,13 +1136,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + await client.close() + + def test_copy_signature(self, async_client: AsyncOpenAI) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + async_client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(async_client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -1138,12 +1155,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, async_client: AsyncOpenAI) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = async_client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -1200,12 +1217,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - async def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + async def test_request_timeout(self, async_client: AsyncOpenAI) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( + request = async_client._build_request( FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) ) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -1220,6 +1237,8 @@ async def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + await client.close() + async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: @@ -1231,6 +1250,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + await client.close() + # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncOpenAI( @@ -1241,6 +1262,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + await client.close() + # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncOpenAI( @@ -1251,6 +1274,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + await client.close() + def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): with httpx.Client() as http_client: @@ -1261,15 +1286,15 @@ def test_invalid_http_client(self) -> None: http_client=cast(Any, http_client), ) - def test_default_headers_option(self) -> None: - client = AsyncOpenAI( + async def test_default_headers_option(self) -> None: + test_client = AsyncOpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = AsyncOpenAI( + test_client2 = AsyncOpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, @@ -1278,10 +1303,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + await test_client.close() + await test_client2.close() + async def test_validate_headers(self) -> None: client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) options = await client._prepare_options(FinalRequestOptions(method="get", url="/foo")) @@ -1293,7 +1321,7 @@ async def test_validate_headers(self) -> None: client2 = AsyncOpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 - def test_default_query_option(self) -> None: + async def test_default_query_option(self) -> None: client = AsyncOpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) @@ -1311,8 +1339,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + await client.close() + + def test_request_extra_json(self, client: OpenAI) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1323,7 +1353,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1334,7 +1364,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1345,8 +1375,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: OpenAI) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1356,7 +1386,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1367,8 +1397,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: OpenAI) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1381,7 +1411,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1395,7 +1425,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1438,7 +1468,7 @@ def test_multipart_repeating_array(self, async_client: AsyncOpenAI) -> None: ] @pytest.mark.respx(base_url=base_url) - async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: class Model1(BaseModel): name: str @@ -1447,12 +1477,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -1463,18 +1493,20 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncOpenAI + ) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -1490,11 +1522,11 @@ class Model(BaseModel): ) ) - response = await self.client.get("/foo", cast_to=Model) + response = await async_client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 - def test_base_url_setter(self) -> None: + async def test_base_url_setter(self) -> None: client = AsyncOpenAI( base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True ) @@ -1504,7 +1536,9 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" - def test_base_url_env(self) -> None: + await client.close() + + async def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): client = AsyncOpenAI(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @@ -1524,7 +1558,7 @@ def test_base_url_env(self) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_trailing_slash(self, client: AsyncOpenAI) -> None: + async def test_base_url_trailing_slash(self, client: AsyncOpenAI) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1533,6 +1567,7 @@ def test_base_url_trailing_slash(self, client: AsyncOpenAI) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1549,7 +1584,7 @@ def test_base_url_trailing_slash(self, client: AsyncOpenAI) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_no_trailing_slash(self, client: AsyncOpenAI) -> None: + async def test_base_url_no_trailing_slash(self, client: AsyncOpenAI) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1558,6 +1593,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncOpenAI) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1574,7 +1610,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncOpenAI) -> None: ], ids=["standard", "custom http client"], ) - def test_absolute_request_url(self, client: AsyncOpenAI) -> None: + async def test_absolute_request_url(self, client: AsyncOpenAI) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1583,37 +1619,37 @@ def test_absolute_request_url(self, client: AsyncOpenAI) -> None: ), ) assert request.url == "https://myapi.com/foo" + await client.close() async def test_copied_client_does_not_close_http(self) -> None: - client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - assert not client.is_closed() + test_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied await asyncio.sleep(0.2) - assert not client.is_closed() + assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - async with client as c2: - assert c2 is client + test_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + async with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - await self.client.get("/foo", cast_to=Model) + await async_client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -1624,19 +1660,17 @@ async def test_client_max_retries_validation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + async def test_default_stream_cls(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: class Model(BaseModel): name: str respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - stream = await self.client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) + stream = await async_client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) assert isinstance(stream, AsyncStream) await stream.response.aclose() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str @@ -1648,11 +1682,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) - response = await client.get("/foo", cast_to=Model) + response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + await strict_client.close() + await non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -1675,13 +1712,12 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - @pytest.mark.asyncio - async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) - + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncOpenAI + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -1700,7 +1736,7 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, model="gpt-4o", ).__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -1717,12 +1753,11 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, ], model="gpt-4o", ).__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( self, @@ -1762,7 +1797,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_omit_retry_count_header( self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1795,7 +1829,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_overwrite_retry_count_header( self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1828,7 +1861,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("openai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_retries_taken_new_response_class( self, async_client: AsyncOpenAI, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1884,26 +1916,26 @@ async def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - await self.client.post( + await async_client.post( "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response ) From 0393d909fc4f45237350747a2cc28162bc7126de Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:14:32 +0000 Subject: [PATCH 155/408] chore(internal): grammar fix (it's -> its) --- src/openai/_utils/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index cddf2c8da4..90494748cc 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -137,7 +137,7 @@ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: # Type safe methods for narrowing types with TypeVars. # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], # however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in it's place. +# care about the contained types we can safely use `object` in its place. # # There are two separate functions defined, `is_*` and `is_*_t` for different use cases. # `is_*` is for when you're dealing with an unknown input From ed9e2ddc5316b5c5cd972b2d9dadde910557fd61 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 23:07:04 +0000 Subject: [PATCH 156/408] feat(api): Realtime API token_limits, Hybrid searching ranking options --- .stats.yml | 6 +-- src/openai/resources/images.py | 30 +++++++++++--- src/openai/resources/realtime/calls.py | 30 ++++++++++++-- .../resources/vector_stores/file_batches.py | 40 +++++++++++++------ .../types/realtime/call_accept_params.py | 15 ++++++- .../realtime_session_create_request.py | 15 ++++++- .../realtime_session_create_request_param.py | 15 ++++++- .../realtime_session_create_response.py | 15 ++++++- .../realtime_truncation_retention_ratio.py | 26 ++++++++++-- ...altime_truncation_retention_ratio_param.py | 25 ++++++++++-- .../types/responses/file_search_tool.py | 16 +++++++- .../types/responses/file_search_tool_param.py | 16 +++++++- .../types/responses/response_output_text.py | 6 +-- .../responses/response_output_text_param.py | 4 +- src/openai/types/responses/tool.py | 2 + src/openai/types/responses/tool_param.py | 2 + .../vector_stores/file_batch_create_params.py | 40 +++++++++++++++++-- src/openai/types/video.py | 3 ++ .../vector_stores/test_file_batches.py | 26 +++++++----- 19 files changed, 273 insertions(+), 59 deletions(-) diff --git a/.stats.yml b/.stats.yml index bc4e084f99..d59fe71ee4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f68f718cd45ac3f9336603601bccc38a718af44d0b26601031de3d0a71b7ce2f.yml -openapi_spec_hash: 1560717860bba4105936647dde8f618d -config_hash: 50ee3382a63c021a9f821a935950e926 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-3c5d1593d7c6f2b38a7d78d7906041465ee9d6e9022f0651e1da194654488108.yml +openapi_spec_hash: 0a4d8ad2469823ce24a3fd94f23f1c2b +config_hash: 032995825500a503a76da119f5354905 diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 9bb332230f..265be6f743 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -168,7 +168,10 @@ def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -282,7 +285,10 @@ def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -392,7 +398,10 @@ def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1046,7 +1055,10 @@ async def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1160,7 +1172,10 @@ async def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1270,7 +1285,10 @@ async def edit( If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + input_fidelity: Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and + `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index a8c4761717..7d2c92fe86 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -195,8 +195,19 @@ def accept( `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. - truncation: Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. + truncation: When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. Clients can configure truncation behavior to truncate with a + lower max token limit, which is an effective way to control token usage and + cost. Truncation will reduce the number of cached tokens on the next turn + (busting the cache), since messages are dropped from the beginning of the + context. However, clients can also configure truncation to retain messages up to + a fraction of the maximum context size, which will reduce the need for future + truncations and thus improve the cache rate. Truncation can be disabled + entirely, which means the server will never truncate but would instead return an + error if the conversation exceeds the model's input token limit. extra_headers: Send extra headers @@ -504,8 +515,19 @@ async def accept( `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. - truncation: Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. + truncation: When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. Clients can configure truncation behavior to truncate with a + lower max token limit, which is an effective way to control token usage and + cost. Truncation will reduce the number of cached tokens on the next turn + (busting the cache), since messages are dropped from the beginning of the + context. However, clients can also configure truncation to retain messages up to + a fraction of the maximum context size, which will reduce the need for future + truncations and thus improve the cache rate. Truncation can be disabled + entirely, which means the server will never truncate but would instead return an + error if the conversation exceeds the model's input token limit. extra_headers: Send extra headers diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index 0f989821de..d31fb59bec 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -52,9 +52,10 @@ def create( self, vector_store_id: str, *, - file_ids: SequenceNotStr[str], attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -66,10 +67,6 @@ def create( Create a vector store file batch. Args: - file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the vector store should use. Useful for tools like `file_search` that can access - files. - attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard. Keys are strings with a maximum @@ -79,6 +76,16 @@ def create( chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. If `attributes` or `chunking_strategy` are provided, they will be applied + to all files in the batch. Mutually exclusive with `files`. + + files: A list of objects that each include a `file_id` plus optional `attributes` or + `chunking_strategy`. Use this when you need to override metadata for specific + files. The global `attributes` or `chunking_strategy` will be ignored and must + be specified for each file. Mutually exclusive with `file_ids`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -94,9 +101,10 @@ def create( f"/vector_stores/{vector_store_id}/file_batches", body=maybe_transform( { - "file_ids": file_ids, "attributes": attributes, "chunking_strategy": chunking_strategy, + "file_ids": file_ids, + "files": files, }, file_batch_create_params.FileBatchCreateParams, ), @@ -389,9 +397,10 @@ async def create( self, vector_store_id: str, *, - file_ids: SequenceNotStr[str], attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -403,10 +412,6 @@ async def create( Create a vector store file batch. Args: - file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that - the vector store should use. Useful for tools like `file_search` that can access - files. - attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard. Keys are strings with a maximum @@ -416,6 +421,16 @@ async def create( chunking_strategy: The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty. + file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that + the vector store should use. Useful for tools like `file_search` that can access + files. If `attributes` or `chunking_strategy` are provided, they will be applied + to all files in the batch. Mutually exclusive with `files`. + + files: A list of objects that each include a `file_id` plus optional `attributes` or + `chunking_strategy`. Use this when you need to override metadata for specific + files. The global `attributes` or `chunking_strategy` will be ignored and must + be specified for each file. Mutually exclusive with `file_ids`. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -431,9 +446,10 @@ async def create( f"/vector_stores/{vector_store_id}/file_batches", body=await async_maybe_transform( { - "file_ids": file_ids, "attributes": attributes, "chunking_strategy": chunking_strategy, + "file_ids": file_ids, + "files": files, }, file_batch_create_params.FileBatchCreateParams, ), diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index 0cfb01e7cf..d6fc92b8e5 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -106,6 +106,17 @@ class CallAcceptParams(TypedDict, total=False): truncation: RealtimeTruncationParam """ - Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. Clients can configure truncation behavior to truncate with a + lower max token limit, which is an effective way to control token usage and + cost. Truncation will reduce the number of cached tokens on the next turn + (busting the cache), since messages are dropped from the beginning of the + context. However, clients can also configure truncation to retain messages up to + a fraction of the maximum context size, which will reduce the need for future + truncations and thus improve the cache rate. Truncation can be disabled + entirely, which means the server will never truncate but would instead return an + error if the conversation exceeds the model's input token limit. """ diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index bc205bd3b5..016ae45b67 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -106,6 +106,17 @@ class RealtimeSessionCreateRequest(BaseModel): truncation: Optional[RealtimeTruncation] = None """ - Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. Clients can configure truncation behavior to truncate with a + lower max token limit, which is an effective way to control token usage and + cost. Truncation will reduce the number of cached tokens on the next turn + (busting the cache), since messages are dropped from the beginning of the + context. However, clients can also configure truncation to retain messages up to + a fraction of the maximum context size, which will reduce the need for future + truncations and thus improve the cache rate. Truncation can be disabled + entirely, which means the server will never truncate but would instead return an + error if the conversation exceeds the model's input token limit. """ diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index d1fa2b35d2..8c3998c1ca 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -106,6 +106,17 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): truncation: RealtimeTruncationParam """ - Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. Clients can configure truncation behavior to truncate with a + lower max token limit, which is an effective way to control token usage and + cost. Truncation will reduce the number of cached tokens on the next turn + (busting the cache), since messages are dropped from the beginning of the + context. However, clients can also configure truncation to retain messages up to + a fraction of the maximum context size, which will reduce the need for future + truncations and thus improve the cache rate. Truncation can be disabled + entirely, which means the server will never truncate but would instead return an + error if the conversation exceeds the model's input token limit. """ diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index bb6b94e900..c1336cd6e4 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -459,6 +459,17 @@ class RealtimeSessionCreateResponse(BaseModel): truncation: Optional[RealtimeTruncation] = None """ - Controls how the realtime conversation is truncated prior to model inference. - The default is `auto`. + When the number of tokens in a conversation exceeds the model's input token + limit, the conversation be truncated, meaning messages (starting from the + oldest) will not be included in the model's context. A 32k context model with + 4,096 max output tokens can only include 28,224 tokens in the context before + truncation occurs. Clients can configure truncation behavior to truncate with a + lower max token limit, which is an effective way to control token usage and + cost. Truncation will reduce the number of cached tokens on the next turn + (busting the cache), since messages are dropped from the beginning of the + context. However, clients can also configure truncation to retain messages up to + a fraction of the maximum context size, which will reduce the need for future + truncations and thus improve the cache rate. Truncation can be disabled + entirely, which means the server will never truncate but would instead return an + error if the conversation exceeds the model's input token limit. """ diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio.py b/src/openai/types/realtime/realtime_truncation_retention_ratio.py index b40427244e..e19ed64831 100644 --- a/src/openai/types/realtime/realtime_truncation_retention_ratio.py +++ b/src/openai/types/realtime/realtime_truncation_retention_ratio.py @@ -1,18 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["RealtimeTruncationRetentionRatio"] +__all__ = ["RealtimeTruncationRetentionRatio", "TokenLimits"] + + +class TokenLimits(BaseModel): + post_instructions: Optional[int] = None + """ + Maximum tokens allowed in the conversation after instructions (which including + tool definitions). For example, setting this to 5,000 would mean that truncation + would occur when the conversation exceeds 5,000 tokens after instructions. This + cannot be higher than the model's context window size minus the maximum output + tokens. + """ class RealtimeTruncationRetentionRatio(BaseModel): retention_ratio: float """ - Fraction of post-instruction conversation tokens to retain (0.0 - 1.0) when the - conversation exceeds the input token limit. + Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when + the conversation exceeds the input token limit. Setting this to `0.8` means that + messages will be dropped until 80% of the maximum allowed tokens are used. This + helps reduce the frequency of truncations and improve cache rates. """ type: Literal["retention_ratio"] """Use retention ratio truncation.""" + + token_limits: Optional[TokenLimits] = None + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py index b65d65666a..4ea80fe4ce 100644 --- a/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py +++ b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py @@ -4,15 +4,34 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["RealtimeTruncationRetentionRatioParam"] +__all__ = ["RealtimeTruncationRetentionRatioParam", "TokenLimits"] + + +class TokenLimits(TypedDict, total=False): + post_instructions: int + """ + Maximum tokens allowed in the conversation after instructions (which including + tool definitions). For example, setting this to 5,000 would mean that truncation + would occur when the conversation exceeds 5,000 tokens after instructions. This + cannot be higher than the model's context window size minus the maximum output + tokens. + """ class RealtimeTruncationRetentionRatioParam(TypedDict, total=False): retention_ratio: Required[float] """ - Fraction of post-instruction conversation tokens to retain (0.0 - 1.0) when the - conversation exceeds the input token limit. + Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when + the conversation exceeds the input token limit. Setting this to `0.8` means that + messages will be dropped until 80% of the maximum allowed tokens are used. This + helps reduce the frequency of truncations and improve cache rates. """ type: Required[Literal["retention_ratio"]] """Use retention ratio truncation.""" + + token_limits: TokenLimits + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ diff --git a/src/openai/types/responses/file_search_tool.py b/src/openai/types/responses/file_search_tool.py index dbdd8cffab..d0d08a323f 100644 --- a/src/openai/types/responses/file_search_tool.py +++ b/src/openai/types/responses/file_search_tool.py @@ -7,12 +7,26 @@ from ..shared.compound_filter import CompoundFilter from ..shared.comparison_filter import ComparisonFilter -__all__ = ["FileSearchTool", "Filters", "RankingOptions"] +__all__ = ["FileSearchTool", "Filters", "RankingOptions", "RankingOptionsHybridSearch"] Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter, None] +class RankingOptionsHybridSearch(BaseModel): + embedding_weight: float + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: float + """The weight of the text in the reciprocal ranking fusion.""" + + class RankingOptions(BaseModel): + hybrid_search: Optional[RankingOptionsHybridSearch] = None + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + ranker: Optional[Literal["auto", "default-2024-11-15"]] = None """The ranker to use for the file search.""" diff --git a/src/openai/types/responses/file_search_tool_param.py b/src/openai/types/responses/file_search_tool_param.py index c7641c1b86..b37a669ebd 100644 --- a/src/openai/types/responses/file_search_tool_param.py +++ b/src/openai/types/responses/file_search_tool_param.py @@ -9,12 +9,26 @@ from ..shared_params.compound_filter import CompoundFilter from ..shared_params.comparison_filter import ComparisonFilter -__all__ = ["FileSearchToolParam", "Filters", "RankingOptions"] +__all__ = ["FileSearchToolParam", "Filters", "RankingOptions", "RankingOptionsHybridSearch"] Filters: TypeAlias = Union[ComparisonFilter, CompoundFilter] +class RankingOptionsHybridSearch(TypedDict, total=False): + embedding_weight: Required[float] + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: Required[float] + """The weight of the text in the reciprocal ranking fusion.""" + + class RankingOptions(TypedDict, total=False): + hybrid_search: RankingOptionsHybridSearch + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + ranker: Literal["auto", "default-2024-11-15"] """The ranker to use for the file search.""" diff --git a/src/openai/types/responses/response_output_text.py b/src/openai/types/responses/response_output_text.py index aa97b629f0..fc579cd894 100644 --- a/src/openai/types/responses/response_output_text.py +++ b/src/openai/types/responses/response_output_text.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import List, Union from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -108,10 +108,10 @@ class ResponseOutputText(BaseModel): annotations: List[Annotation] """The annotations of the text output.""" + logprobs: List[Logprob] + text: str """The text output from the model.""" type: Literal["output_text"] """The type of the output text. Always `output_text`.""" - - logprobs: Optional[List[Logprob]] = None diff --git a/src/openai/types/responses/response_output_text_param.py b/src/openai/types/responses/response_output_text_param.py index 63d2d394a8..445a308a5b 100644 --- a/src/openai/types/responses/response_output_text_param.py +++ b/src/openai/types/responses/response_output_text_param.py @@ -106,10 +106,10 @@ class ResponseOutputTextParam(TypedDict, total=False): annotations: Required[Iterable[Annotation]] """The annotations of the text output.""" + logprobs: Required[Iterable[Logprob]] + text: Required[str] """The text output from the model.""" type: Required[Literal["output_text"]] """The type of the output text. Always `output_text`.""" - - logprobs: Iterable[Logprob] diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 6239b818c9..b29fede0c9 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -161,6 +161,8 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): file_ids: Optional[List[str]] = None """An optional list of uploaded files to make available to your code.""" + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index ff4ac2b953..dd1ea0bd54 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -161,6 +161,8 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): file_ids: SequenceNotStr[str] """An optional list of uploaded files to make available to your code.""" + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] diff --git a/src/openai/types/vector_stores/file_batch_create_params.py b/src/openai/types/vector_stores/file_batch_create_params.py index d8d7b44888..2ab98a83ab 100644 --- a/src/openai/types/vector_stores/file_batch_create_params.py +++ b/src/openai/types/vector_stores/file_batch_create_params.py @@ -2,20 +2,54 @@ from __future__ import annotations -from typing import Dict, Union, Optional +from typing import Dict, Union, Iterable, Optional from typing_extensions import Required, TypedDict from ..._types import SequenceNotStr from ..file_chunking_strategy_param import FileChunkingStrategyParam -__all__ = ["FileBatchCreateParams"] +__all__ = ["FileBatchCreateParams", "File"] class FileBatchCreateParams(TypedDict, total=False): - file_ids: Required[SequenceNotStr[str]] + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + chunking_strategy: FileChunkingStrategyParam + """The chunking strategy used to chunk the file(s). + + If not set, will use the `auto` strategy. Only applicable if `file_ids` is + non-empty. + """ + + file_ids: SequenceNotStr[str] """ A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access + files. If `attributes` or `chunking_strategy` are provided, they will be applied + to all files in the batch. Mutually exclusive with `files`. + """ + + files: Iterable[File] + """ + A list of objects that each include a `file_id` plus optional `attributes` or + `chunking_strategy`. Use this when you need to override metadata for specific + files. The global `attributes` or `chunking_strategy` will be ignored and must + be specified for each file. Mutually exclusive with `file_ids`. + """ + + +class File(TypedDict, total=False): + file_id: Required[str] + """ + A [File](https://platform.openai.com/docs/api-reference/files) ID that the + vector store should use. Useful for tools like `file_search` that can access files. """ diff --git a/src/openai/types/video.py b/src/openai/types/video.py index 2c804f75b8..22ee3a11f7 100644 --- a/src/openai/types/video.py +++ b/src/openai/types/video.py @@ -37,6 +37,9 @@ class Video(BaseModel): progress: int """Approximate completion percentage for the generation task.""" + prompt: Optional[str] = None + """The prompt that was used to generate the video.""" + remixed_from_video_id: Optional[str] = None """Identifier of the source video if this video is a remix.""" diff --git a/tests/api_resources/vector_stores/test_file_batches.py b/tests/api_resources/vector_stores/test_file_batches.py index ac678ce912..abbefc20e9 100644 --- a/tests/api_resources/vector_stores/test_file_batches.py +++ b/tests/api_resources/vector_stores/test_file_batches.py @@ -25,7 +25,6 @@ class TestFileBatches: def test_method_create(self, client: OpenAI) -> None: file_batch = client.vector_stores.file_batches.create( vector_store_id="vs_abc123", - file_ids=["string"], ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @@ -33,9 +32,16 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: file_batch = client.vector_stores.file_batches.create( vector_store_id="vs_abc123", - file_ids=["string"], attributes={"foo": "string"}, chunking_strategy={"type": "auto"}, + file_ids=["string"], + files=[ + { + "file_id": "file_id", + "attributes": {"foo": "string"}, + "chunking_strategy": {"type": "auto"}, + } + ], ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @@ -43,7 +49,6 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.vector_stores.file_batches.with_raw_response.create( vector_store_id="vs_abc123", - file_ids=["string"], ) assert response.is_closed is True @@ -55,7 +60,6 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.vector_stores.file_batches.with_streaming_response.create( vector_store_id="vs_abc123", - file_ids=["string"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -70,7 +74,6 @@ def test_path_params_create(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): client.vector_stores.file_batches.with_raw_response.create( vector_store_id="", - file_ids=["string"], ) @parametrize @@ -240,7 +243,6 @@ class TestAsyncFileBatches: async def test_method_create(self, async_client: AsyncOpenAI) -> None: file_batch = await async_client.vector_stores.file_batches.create( vector_store_id="vs_abc123", - file_ids=["string"], ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @@ -248,9 +250,16 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: file_batch = await async_client.vector_stores.file_batches.create( vector_store_id="vs_abc123", - file_ids=["string"], attributes={"foo": "string"}, chunking_strategy={"type": "auto"}, + file_ids=["string"], + files=[ + { + "file_id": "file_id", + "attributes": {"foo": "string"}, + "chunking_strategy": {"type": "auto"}, + } + ], ) assert_matches_type(VectorStoreFileBatch, file_batch, path=["response"]) @@ -258,7 +267,6 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.vector_stores.file_batches.with_raw_response.create( vector_store_id="vs_abc123", - file_ids=["string"], ) assert response.is_closed is True @@ -270,7 +278,6 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.vector_stores.file_batches.with_streaming_response.create( vector_store_id="vs_abc123", - file_ids=["string"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -285,7 +292,6 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"): await async_client.vector_stores.file_batches.with_raw_response.create( vector_store_id="", - file_ids=["string"], ) @parametrize From c82714c765a51a33ce64e8f0c646c47462796e1c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 23:07:37 +0000 Subject: [PATCH 157/408] release: 2.7.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2f8909f197..d1328ca9c9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.6.1" + ".": "2.7.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ce541566d..516368cfed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 2.7.0 (2025-11-03) + +Full Changelog: [v2.6.1...v2.7.0](https://github.com/openai/openai-python/compare/v2.6.1...v2.7.0) + +### Features + +* **api:** Realtime API token_limits, Hybrid searching ranking options ([5b43992](https://github.com/openai/openai-python/commit/5b4399219d7ed326411aec524d25ef2b8e3152fc)) +* **api:** remove InputAudio from ResponseInputContent ([bd70a33](https://github.com/openai/openai-python/commit/bd70a33234741fa68c185105e4f53cc0275a2a50)) + + +### Bug Fixes + +* **client:** close streams without requiring full consumption ([d8bb7d6](https://github.com/openai/openai-python/commit/d8bb7d6d728c5481de4198eebe668b67803ae14a)) +* **readme:** update realtime examples ([#2714](https://github.com/openai/openai-python/issues/2714)) ([d0370a8](https://github.com/openai/openai-python/commit/d0370a8d61fc2f710a34d8aad48f649a9683106d)) +* **uploads:** avoid file handle leak ([4f1b691](https://github.com/openai/openai-python/commit/4f1b691ab4db41aebd397ec41942b43fb0f0743c)) + + +### Chores + +* **internal/tests:** avoid race condition with implicit client cleanup ([933d23b](https://github.com/openai/openai-python/commit/933d23bd8d7809c77e0796becfe052167d44d40a)) +* **internal:** grammar fix (it's -> its) ([f7e9e9e](https://github.com/openai/openai-python/commit/f7e9e9e4f43039f19a41375a6d2b2bdc2264dad7)) + ## 2.6.1 (2025-10-24) Full Changelog: [v2.6.0...v2.6.1](https://github.com/openai/openai-python/compare/v2.6.0...v2.6.1) diff --git a/pyproject.toml b/pyproject.toml index e96101b51c..8ff272e18e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.6.1" +version = "2.7.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b0fe817996..0963c9c373 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.6.1" # x-release-please-version +__version__ = "2.7.0" # x-release-please-version From e2d25ef236eefe7e34d86bdfd831bc7068cb8510 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 06:02:50 +0000 Subject: [PATCH 158/408] fix(api): fix nullability of logprobs Makes ResponseOutputText.logprobs nullable, matching with 2.6.1. While this is always present in the server response, this inadvertently affected params and some constructors --- .stats.yml | 4 ++-- src/openai/types/responses/response_output_text.py | 6 +++--- src/openai/types/responses/response_output_text_param.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.stats.yml b/.stats.yml index d59fe71ee4..2588839c5a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-3c5d1593d7c6f2b38a7d78d7906041465ee9d6e9022f0651e1da194654488108.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-eeba8addf3a5f412e5ce8d22031e60c61650cee3f5d9e587a2533f6818a249ea.yml openapi_spec_hash: 0a4d8ad2469823ce24a3fd94f23f1c2b -config_hash: 032995825500a503a76da119f5354905 +config_hash: 0bb1941a78ece0b610a2fbba7d74a84c diff --git a/src/openai/types/responses/response_output_text.py b/src/openai/types/responses/response_output_text.py index fc579cd894..aa97b629f0 100644 --- a/src/openai/types/responses/response_output_text.py +++ b/src/openai/types/responses/response_output_text.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union +from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -108,10 +108,10 @@ class ResponseOutputText(BaseModel): annotations: List[Annotation] """The annotations of the text output.""" - logprobs: List[Logprob] - text: str """The text output from the model.""" type: Literal["output_text"] """The type of the output text. Always `output_text`.""" + + logprobs: Optional[List[Logprob]] = None diff --git a/src/openai/types/responses/response_output_text_param.py b/src/openai/types/responses/response_output_text_param.py index 445a308a5b..63d2d394a8 100644 --- a/src/openai/types/responses/response_output_text_param.py +++ b/src/openai/types/responses/response_output_text_param.py @@ -106,10 +106,10 @@ class ResponseOutputTextParam(TypedDict, total=False): annotations: Required[Iterable[Annotation]] """The annotations of the text output.""" - logprobs: Required[Iterable[Logprob]] - text: Required[str] """The text output from the model.""" type: Required[Literal["output_text"]] """The type of the output text. Always `output_text`.""" + + logprobs: Iterable[Logprob] From 6574bcd612771186995074846253caa0ff1ba517 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 06:03:21 +0000 Subject: [PATCH 159/408] release: 2.7.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d1328ca9c9..0a163d740c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.7.0" + ".": "2.7.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 516368cfed..d0b673d0f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.7.1 (2025-11-04) + +Full Changelog: [v2.7.0...v2.7.1](https://github.com/openai/openai-python/compare/v2.7.0...v2.7.1) + +### Bug Fixes + +* **api:** fix nullability of logprobs ([373b7f6](https://github.com/openai/openai-python/commit/373b7f6e4255dfef3ccd92520011e8ba44e8b7f0)) + ## 2.7.0 (2025-11-03) Full Changelog: [v2.6.1...v2.7.0](https://github.com/openai/openai-python/compare/v2.6.1...v2.7.0) diff --git a/pyproject.toml b/pyproject.toml index 8ff272e18e..a4850a0f49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.7.0" +version = "2.7.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0963c9c373..9fb4c23dba 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.7.0" # x-release-please-version +__version__ = "2.7.1" # x-release-please-version From 3f52ac842f9fc20d9729b71523a09f10374f97b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:28:54 +0000 Subject: [PATCH 160/408] chore(package): drop Python 3.8 support --- README.md | 4 ++-- pyproject.toml | 5 ++--- src/openai/_utils/_sync.py | 34 +++------------------------------- 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 21f79312a1..470707e1f3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) -The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.8+ +The OpenAI Python library provides convenient access to the OpenAI REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -854,7 +854,7 @@ print(openai.__version__) ## Requirements -Python 3.8 or higher. +Python 3.9 or higher. ## Contributing diff --git a/pyproject.toml b/pyproject.toml index a4850a0f49..bbc00e2d0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,11 +17,10 @@ dependencies = [ "tqdm > 4", "jiter>=0.10.0, <1", ] -requires-python = ">= 3.8" +requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -159,7 +158,7 @@ format-command="ruff format --stdin-filename {filename}" # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.8" +pythonVersion = "3.9" exclude = [ "_dev", diff --git a/src/openai/_utils/_sync.py b/src/openai/_utils/_sync.py index ad7ec71b76..f6027c183d 100644 --- a/src/openai/_utils/_sync.py +++ b/src/openai/_utils/_sync.py @@ -1,10 +1,8 @@ from __future__ import annotations -import sys import asyncio import functools -import contextvars -from typing import Any, TypeVar, Callable, Awaitable +from typing import TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio @@ -15,34 +13,11 @@ T_ParamSpec = ParamSpec("T_ParamSpec") -if sys.version_info >= (3, 9): - _asyncio_to_thread = asyncio.to_thread -else: - # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread - # for Python 3.8 support - async def _asyncio_to_thread( - func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs - ) -> Any: - """Asynchronously run function *func* in a separate thread. - - Any *args and **kwargs supplied for this function are directly passed - to *func*. Also, the current :class:`contextvars.Context` is propagated, - allowing context variables from the main thread to be accessed in the - separate thread. - - Returns a coroutine that can be awaited to get the eventual result of *func*. - """ - loop = asyncio.events.get_running_loop() - ctx = contextvars.copy_context() - func_call = functools.partial(ctx.run, func, *args, **kwargs) - return await loop.run_in_executor(None, func_call) - - async def to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": - return await _asyncio_to_thread(func, *args, **kwargs) + return await asyncio.to_thread(func, *args, **kwargs) return await anyio.to_thread.run_sync( functools.partial(func, *args, **kwargs), @@ -53,10 +28,7 @@ async def to_thread( def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same - positional and keyword arguments. For python version 3.9 and above, it uses - asyncio.to_thread to run the function in a separate thread. For python version - 3.8, it uses locally defined copy of the asyncio.to_thread function which was - introduced in python 3.9. + positional and keyword arguments. Usage: From 080587bda94771e54f39171067c165db597b950f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:35:58 +0000 Subject: [PATCH 161/408] fix: compat with Python 3.14 --- src/openai/_models.py | 11 ++++++++--- tests/test_models.py | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index af71a91850..0f091058d2 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -2,6 +2,7 @@ import os import inspect +import weakref from typing import TYPE_CHECKING, Any, Type, Tuple, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( @@ -598,6 +599,9 @@ class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. @@ -640,8 +644,9 @@ def __init__( def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached discriminator_field_name: str | None = None @@ -694,7 +699,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) - cast(CachedDiscriminatorType, union).__discriminator__ = details + DISCRIMINATOR_CACHE.setdefault(union, details) return details diff --git a/tests/test_models.py b/tests/test_models.py index 410ec3bf4e..588869ee35 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -9,7 +9,7 @@ from openai._utils import PropertyInfo from openai._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from openai._models import BaseModel, construct_type +from openai._models import DISCRIMINATOR_CACHE, BaseModel, construct_type class BasicModel(BaseModel): @@ -809,7 +809,7 @@ class B(BaseModel): UnionType = cast(Any, Union[A, B]) - assert not hasattr(UnionType, "__discriminator__") + assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) @@ -818,7 +818,7 @@ class B(BaseModel): assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] - discriminator = UnionType.__discriminator__ + discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( @@ -830,7 +830,7 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache - assert UnionType.__discriminator__ is discriminator + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") From 650be393dedf2a4550092817c2b82c1d04d6e9dc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:36:34 +0000 Subject: [PATCH 162/408] release: 2.7.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0a163d740c..e3ec769daf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.7.1" + ".": "2.7.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d0b673d0f7..03dfa94242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.7.2 (2025-11-10) + +Full Changelog: [v2.7.1...v2.7.2](https://github.com/openai/openai-python/compare/v2.7.1...v2.7.2) + +### Bug Fixes + +* compat with Python 3.14 ([15a7ec8](https://github.com/openai/openai-python/commit/15a7ec8a753d7f57d525fca60c547fd5331cb214)) + + +### Chores + +* **package:** drop Python 3.8 support ([afc14f2](https://github.com/openai/openai-python/commit/afc14f2e42e7a8174f2ff1a5672829b808a716bf)) + ## 2.7.1 (2025-11-04) Full Changelog: [v2.7.0...v2.7.1](https://github.com/openai/openai-python/compare/v2.7.0...v2.7.1) diff --git a/pyproject.toml b/pyproject.toml index bbc00e2d0c..ca13765a98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.7.1" +version = "2.7.2" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9fb4c23dba..6c0fcb3469 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.7.1" # x-release-please-version +__version__ = "2.7.2" # x-release-please-version From 22317c6eb1b34428f21b801ecbb856f1625774a2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:40:03 +0000 Subject: [PATCH 163/408] fix(compat): update signatures of `model_dump` and `model_dump_json` for Pydantic v1 --- src/openai/_models.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 0f091058d2..fac59c2cb8 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -282,15 +282,16 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -298,16 +299,24 @@ def model_dump( Args: mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. @@ -324,6 +333,8 @@ def model_dump( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -340,15 +351,17 @@ def model_dump_json( self, *, indent: int | None = None, + ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: @@ -380,6 +393,10 @@ def model_dump_json( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, From 139b376ef9a186285e3bca891ebcdf57efc1ca75 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:47:02 +0000 Subject: [PATCH 164/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 2588839c5a..25f1a92629 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-eeba8addf3a5f412e5ce8d22031e60c61650cee3f5d9e587a2533f6818a249ea.yml openapi_spec_hash: 0a4d8ad2469823ce24a3fd94f23f1c2b -config_hash: 0bb1941a78ece0b610a2fbba7d74a84c +config_hash: 630eea84bb3067d25640419af058ed56 From ac534dcd13fe1178c0d7e95f0cef3dbdcd647418 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:10:43 +0000 Subject: [PATCH 165/408] feat(api): gpt 5.1 --- .stats.yml | 6 +- api.md | 9 + src/openai/lib/_parsing/_responses.py | 4 + src/openai/resources/batches.py | 22 ++- src/openai/resources/beta/assistants.py | 58 ++++--- .../resources/beta/threads/runs/runs.py | 86 ++++++---- .../resources/chat/completions/completions.py | 132 +++++++++++---- src/openai/resources/responses/responses.py | 52 ++++++ src/openai/types/batch_create_params.py | 12 +- .../types/beta/assistant_create_params.py | 16 +- .../types/beta/assistant_update_params.py | 16 +- .../types/beta/threads/run_create_params.py | 16 +- .../types/chat/completion_create_params.py | 24 ++- .../types/conversations/conversation_item.py | 8 + ...create_eval_completions_run_data_source.py | 16 +- ..._eval_completions_run_data_source_param.py | 16 +- src/openai/types/evals/run_cancel_response.py | 32 ++-- src/openai/types/evals/run_create_params.py | 32 ++-- src/openai/types/evals/run_create_response.py | 32 ++-- src/openai/types/evals/run_list_response.py | 32 ++-- .../types/evals/run_retrieve_response.py | 32 ++-- .../types/graders/score_model_grader.py | 16 +- .../types/graders/score_model_grader_param.py | 16 +- src/openai/types/responses/__init__.py | 20 +++ .../types/responses/apply_patch_tool.py | 12 ++ .../types/responses/apply_patch_tool_param.py | 12 ++ .../types/responses/function_shell_tool.py | 12 ++ .../responses/function_shell_tool_param.py | 12 ++ .../responses/input_token_count_params.py | 4 + src/openai/types/responses/parsed_response.py | 8 + src/openai/types/responses/response.py | 19 ++- .../response_apply_patch_tool_call.py | 76 +++++++++ .../response_apply_patch_tool_call_output.py | 31 ++++ .../types/responses/response_create_params.py | 12 ++ ...onse_function_shell_call_output_content.py | 36 ++++ ...unction_shell_call_output_content_param.py | 35 ++++ .../response_function_shell_tool_call.py | 44 +++++ ...esponse_function_shell_tool_call_output.py | 70 ++++++++ .../types/responses/response_input_item.py | 159 ++++++++++++++++++ .../responses/response_input_item_param.py | 158 +++++++++++++++++ .../types/responses/response_input_param.py | 158 +++++++++++++++++ src/openai/types/responses/response_item.py | 8 + .../types/responses/response_output_item.py | 8 + src/openai/types/responses/tool.py | 4 + .../responses/tool_choice_apply_patch.py | 12 ++ .../tool_choice_apply_patch_param.py | 12 ++ .../types/responses/tool_choice_shell.py | 12 ++ .../responses/tool_choice_shell_param.py | 12 ++ src/openai/types/responses/tool_param.py | 4 + src/openai/types/shared/chat_model.py | 5 + src/openai/types/shared/reasoning.py | 16 +- src/openai/types/shared/reasoning_effort.py | 2 +- src/openai/types/shared_params/chat_model.py | 5 + src/openai/types/shared_params/reasoning.py | 16 +- .../types/shared_params/reasoning_effort.py | 2 +- tests/api_resources/beta/test_assistants.py | 8 +- tests/api_resources/beta/threads/test_runs.py | 8 +- tests/api_resources/chat/test_completions.py | 12 +- .../responses/test_input_tokens.py | 4 +- tests/api_resources/test_responses.py | 12 +- 60 files changed, 1486 insertions(+), 239 deletions(-) create mode 100644 src/openai/types/responses/apply_patch_tool.py create mode 100644 src/openai/types/responses/apply_patch_tool_param.py create mode 100644 src/openai/types/responses/function_shell_tool.py create mode 100644 src/openai/types/responses/function_shell_tool_param.py create mode 100644 src/openai/types/responses/response_apply_patch_tool_call.py create mode 100644 src/openai/types/responses/response_apply_patch_tool_call_output.py create mode 100644 src/openai/types/responses/response_function_shell_call_output_content.py create mode 100644 src/openai/types/responses/response_function_shell_call_output_content_param.py create mode 100644 src/openai/types/responses/response_function_shell_tool_call.py create mode 100644 src/openai/types/responses/response_function_shell_tool_call_output.py create mode 100644 src/openai/types/responses/tool_choice_apply_patch.py create mode 100644 src/openai/types/responses/tool_choice_apply_patch_param.py create mode 100644 src/openai/types/responses/tool_choice_shell.py create mode 100644 src/openai/types/responses/tool_choice_shell_param.py diff --git a/.stats.yml b/.stats.yml index 25f1a92629..b44bda286e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-eeba8addf3a5f412e5ce8d22031e60c61650cee3f5d9e587a2533f6818a249ea.yml -openapi_spec_hash: 0a4d8ad2469823ce24a3fd94f23f1c2b -config_hash: 630eea84bb3067d25640419af058ed56 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-ca24bc4d8125b5153514ce643c4e3220f25971b7d67ca384d56d493c72c0d977.yml +openapi_spec_hash: c6f048c7b3d29f4de48fde0e845ba33f +config_hash: b876221dfb213df9f0a999e75d38a65e diff --git a/api.md b/api.md index 96642c01ad..28ee551af3 100644 --- a/api.md +++ b/api.md @@ -732,12 +732,16 @@ Types: ```python from openai.types.responses import ( + ApplyPatchTool, ComputerTool, CustomTool, EasyInputMessage, FileSearchTool, + FunctionShellTool, FunctionTool, Response, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, ResponseAudioDeltaEvent, ResponseAudioDoneEvent, ResponseAudioTranscriptDeltaEvent, @@ -774,6 +778,9 @@ from openai.types.responses import ( ResponseFunctionCallArgumentsDoneEvent, ResponseFunctionCallOutputItem, ResponseFunctionCallOutputItemList, + ResponseFunctionShellCallOutputContent, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, ResponseFunctionToolCall, ResponseFunctionToolCallItem, ResponseFunctionToolCallOutputItem, @@ -836,10 +843,12 @@ from openai.types.responses import ( ResponseWebSearchCallSearchingEvent, Tool, ToolChoiceAllowed, + ToolChoiceApplyPatch, ToolChoiceCustom, ToolChoiceFunction, ToolChoiceMcp, ToolChoiceOptions, + ToolChoiceShell, ToolChoiceTypes, WebSearchPreviewTool, WebSearchTool, diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 8a1bf3cf2c..4d7b0b6224 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -108,6 +108,10 @@ def parse_response( or output.type == "image_generation_call" or output.type == "code_interpreter_call" or output.type == "local_shell_call" + or output.type == "shell_call" + or output.type == "shell_call_output" + or output.type == "apply_patch_call" + or output.type == "apply_patch_call_output" or output.type == "mcp_list_tools" or output.type == "exec" or output.type == "custom_tool_call" diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index afc7fa6eb9..80400839e4 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -46,7 +46,9 @@ def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], + endpoint: Literal[ + "/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations" + ], input_file_id: str, metadata: Optional[Metadata] | Omit = omit, output_expires_after: batch_create_params.OutputExpiresAfter | Omit = omit, @@ -65,9 +67,10 @@ def create( is supported. endpoint: The endpoint to be used for all requests in the batch. Currently - `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` - are supported. Note that `/v1/embeddings` batches are also restricted to a - maximum of 50,000 embedding inputs across all requests in the batch. + `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, + and `/v1/moderations` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. @@ -261,7 +264,9 @@ async def create( self, *, completion_window: Literal["24h"], - endpoint: Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"], + endpoint: Literal[ + "/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations" + ], input_file_id: str, metadata: Optional[Metadata] | Omit = omit, output_expires_after: batch_create_params.OutputExpiresAfter | Omit = omit, @@ -280,9 +285,10 @@ async def create( is supported. endpoint: The endpoint to be used for all requests in the batch. Currently - `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` - are supported. Note that `/v1/embeddings` batches are also restricted to a - maximum of 50,000 embedding inputs across all requests in the batch. + `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, + and `/v1/moderations` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index a958c0caa1..e4ec1dca11 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -98,12 +98,16 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -308,12 +312,16 @@ def update( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -557,12 +565,16 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -767,12 +779,16 @@ async def update( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 2753f5817e..d7445d52b5 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -169,12 +169,16 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -326,12 +330,16 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -479,12 +487,16 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1608,12 +1620,16 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1765,12 +1781,16 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1918,12 +1938,16 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 4b73c69ae9..c205011d10 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -102,6 +102,7 @@ def parse( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, @@ -201,6 +202,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), "safety_identifier": safety_identifier, @@ -255,6 +257,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -401,14 +404,23 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: An object specifying the format that the model must output. @@ -547,6 +559,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -701,14 +714,23 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: An object specifying the format that the model must output. @@ -838,6 +860,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -992,14 +1015,23 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: An object specifying the format that the model must output. @@ -1128,6 +1160,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -1174,6 +1207,7 @@ def create( "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": response_format, "safety_identifier": safety_identifier, @@ -1407,6 +1441,7 @@ def stream( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, @@ -1477,6 +1512,7 @@ def stream( prediction=prediction, presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, + prompt_cache_retention=prompt_cache_retention, reasoning_effort=reasoning_effort, safety_identifier=safety_identifier, seed=seed, @@ -1549,6 +1585,7 @@ async def parse( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, @@ -1648,6 +1685,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), "safety_identifier": safety_identifier, @@ -1702,6 +1740,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -1848,14 +1887,23 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: An object specifying the format that the model must output. @@ -1994,6 +2042,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -2148,14 +2197,23 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: An object specifying the format that the model must output. @@ -2285,6 +2343,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -2439,14 +2498,23 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. response_format: An object specifying the format that the model must output. @@ -2575,6 +2643,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -2621,6 +2690,7 @@ async def create( "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": response_format, "safety_identifier": safety_identifier, @@ -2854,6 +2924,7 @@ def stream( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, @@ -2925,6 +2996,7 @@ def stream( prediction=prediction, presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, + prompt_cache_retention=prompt_cache_retention, reasoning_effort=reasoning_effort, safety_identifier=safety_identifier, seed=seed, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 439cf8d3ad..dcf87ba07c 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -100,6 +100,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -215,6 +216,11 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning: **gpt-5 and o-series models only** Configuration options for @@ -340,6 +346,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -461,6 +468,11 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning: **gpt-5 and o-series models only** Configuration options for @@ -579,6 +591,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -700,6 +713,11 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning: **gpt-5 and o-series models only** Configuration options for @@ -816,6 +834,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -854,6 +873,7 @@ def create( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, "service_tier": service_tier, @@ -915,6 +935,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -954,6 +975,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -987,6 +1009,7 @@ def stream( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, "service_tier": service_tier, @@ -1040,6 +1063,7 @@ def stream( previous_response_id=previous_response_id, prompt=prompt, prompt_cache_key=prompt_cache_key, + prompt_cache_retention=prompt_cache_retention, store=store, stream_options=stream_options, stream=True, @@ -1098,6 +1122,7 @@ def parse( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -1155,6 +1180,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, "service_tier": service_tier, @@ -1535,6 +1561,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -1650,6 +1677,11 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning: **gpt-5 and o-series models only** Configuration options for @@ -1775,6 +1807,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -1896,6 +1929,11 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning: **gpt-5 and o-series models only** Configuration options for @@ -2014,6 +2052,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2135,6 +2174,11 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + reasoning: **gpt-5 and o-series models only** Configuration options for @@ -2251,6 +2295,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2289,6 +2334,7 @@ async def create( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, "service_tier": service_tier, @@ -2350,6 +2396,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2389,6 +2436,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2422,6 +2470,7 @@ def stream( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, "service_tier": service_tier, @@ -2476,6 +2525,7 @@ def stream( previous_response_id=previous_response_id, prompt=prompt, prompt_cache_key=prompt_cache_key, + prompt_cache_retention=prompt_cache_retention, store=store, stream_options=stream_options, temperature=temperature, @@ -2538,6 +2588,7 @@ async def parse( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2595,6 +2646,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, "service_tier": service_tier, diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index c0f9034d5e..c182a87e7f 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -17,13 +17,15 @@ class BatchCreateParams(TypedDict, total=False): Currently only `24h` is supported. """ - endpoint: Required[Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions"]] + endpoint: Required[ + Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations"] + ] """The endpoint to be used for all requests in the batch. - Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, and - `/v1/completions` are supported. Note that `/v1/embeddings` batches are also - restricted to a maximum of 50,000 embedding inputs across all requests in the - batch. + Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, + `/v1/completions`, and `/v1/moderations` are supported. Note that + `/v1/embeddings` batches are also restricted to a maximum of 50,000 embedding + inputs across all requests in the batch. """ input_file_id: Required[str] diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 6fb1551fa5..009b0f49e3 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -62,12 +62,16 @@ class AssistantCreateParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 6d20b8e01f..432116ad52 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -97,12 +97,16 @@ class AssistantUpdateParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 3190c8b308..74786d7d5c 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -111,12 +111,16 @@ class RunCreateParamsBase(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 8b0fdd04b3..e02c06cbb0 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -185,16 +185,28 @@ class CompletionCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] + """The retention policy for the prompt cache. + + Set to `24h` to enable extended prompt caching, which keeps cached prefixes + active for longer, up to a maximum of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + """ + reasoning_effort: Optional[ReasoningEffort] """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ response_format: ResponseFormat diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py index 9e9fb40033..052d09ce77 100644 --- a/src/openai/types/conversations/conversation_item.py +++ b/src/openai/types/conversations/conversation_item.py @@ -10,12 +10,16 @@ from ..responses.response_custom_tool_call import ResponseCustomToolCall from ..responses.response_computer_tool_call import ResponseComputerToolCall from ..responses.response_function_web_search import ResponseFunctionWebSearch +from ..responses.response_apply_patch_tool_call import ResponseApplyPatchToolCall from ..responses.response_file_search_tool_call import ResponseFileSearchToolCall from ..responses.response_custom_tool_call_output import ResponseCustomToolCallOutput from ..responses.response_function_tool_call_item import ResponseFunctionToolCallItem +from ..responses.response_function_shell_tool_call import ResponseFunctionShellToolCall from ..responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from ..responses.response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput from ..responses.response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem from ..responses.response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem +from ..responses.response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput __all__ = [ "ConversationItem", @@ -211,6 +215,10 @@ class McpCall(BaseModel): ResponseCodeInterpreterToolCall, LocalShellCall, LocalShellCallOutput, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, McpListTools, McpApprovalRequest, McpApprovalResponse, diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index a9f2fd0858..742c27a775 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -172,12 +172,16 @@ class SamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ response_format: Optional[SamplingParamsResponseFormat] = None diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index e682e2db5e..18cd5018b1 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -168,12 +168,16 @@ class SamplingParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ response_format: SamplingParamsResponseFormat diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 084dd6ce5c..b18598b20e 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -103,12 +103,16 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ temperature: Optional[float] = None @@ -241,12 +245,16 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index f114fae6a4..a50433f06d 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -116,12 +116,16 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ temperature: Optional[float] @@ -259,12 +263,16 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ seed: int diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 1343335e0d..41dac615c7 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -103,12 +103,16 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ temperature: Optional[float] = None @@ -241,12 +245,16 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index 7c32ce54a2..61bff95447 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -103,12 +103,16 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ temperature: Optional[float] = None @@ -241,12 +245,16 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index f1212c1671..651d7423a9 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -103,12 +103,16 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ temperature: Optional[float] = None @@ -241,12 +245,16 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index 35e2dc1468..84686a9642 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -67,12 +67,16 @@ class SamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 168feeae13..aec7a95ad4 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -73,12 +73,16 @@ class SamplingParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ seed: Optional[int] diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index fd70836e50..e707141d9a 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -23,13 +23,16 @@ from .response_status import ResponseStatus as ResponseStatus from .tool_choice_mcp import ToolChoiceMcp as ToolChoiceMcp from .web_search_tool import WebSearchTool as WebSearchTool +from .apply_patch_tool import ApplyPatchTool as ApplyPatchTool from .file_search_tool import FileSearchTool as FileSearchTool from .custom_tool_param import CustomToolParam as CustomToolParam +from .tool_choice_shell import ToolChoiceShell as ToolChoiceShell from .tool_choice_types import ToolChoiceTypes as ToolChoiceTypes from .easy_input_message import EasyInputMessage as EasyInputMessage from .response_item_list import ResponseItemList as ResponseItemList from .tool_choice_custom import ToolChoiceCustom as ToolChoiceCustom from .computer_tool_param import ComputerToolParam as ComputerToolParam +from .function_shell_tool import FunctionShellTool as FunctionShellTool from .function_tool_param import FunctionToolParam as FunctionToolParam from .response_includable import ResponseIncludable as ResponseIncludable from .response_input_file import ResponseInputFile as ResponseInputFile @@ -51,6 +54,7 @@ from .response_stream_event import ResponseStreamEvent as ResponseStreamEvent from .tool_choice_mcp_param import ToolChoiceMcpParam as ToolChoiceMcpParam from .web_search_tool_param import WebSearchToolParam as WebSearchToolParam +from .apply_patch_tool_param import ApplyPatchToolParam as ApplyPatchToolParam from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam from .input_item_list_params import InputItemListParams as InputItemListParams from .response_create_params import ResponseCreateParams as ResponseCreateParams @@ -59,6 +63,8 @@ from .response_output_message import ResponseOutputMessage as ResponseOutputMessage from .response_output_refusal import ResponseOutputRefusal as ResponseOutputRefusal from .response_reasoning_item import ResponseReasoningItem as ResponseReasoningItem +from .tool_choice_apply_patch import ToolChoiceApplyPatch as ToolChoiceApplyPatch +from .tool_choice_shell_param import ToolChoiceShellParam as ToolChoiceShellParam from .tool_choice_types_param import ToolChoiceTypesParam as ToolChoiceTypesParam from .web_search_preview_tool import WebSearchPreviewTool as WebSearchPreviewTool from .easy_input_message_param import EasyInputMessageParam as EasyInputMessageParam @@ -67,6 +73,7 @@ from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent from .tool_choice_custom_param import ToolChoiceCustomParam as ToolChoiceCustomParam +from .function_shell_tool_param import FunctionShellToolParam as FunctionShellToolParam from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent from .response_custom_tool_call import ResponseCustomToolCall as ResponseCustomToolCall from .response_incomplete_event import ResponseIncompleteEvent as ResponseIncompleteEvent @@ -98,7 +105,9 @@ from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam as ToolChoiceApplyPatchParam from .web_search_preview_tool_param import WebSearchPreviewToolParam as WebSearchPreviewToolParam +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall as ResponseApplyPatchToolCall from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent from .response_custom_tool_call_param import ResponseCustomToolCallParam as ResponseCustomToolCallParam @@ -110,6 +119,7 @@ from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent from .response_format_text_config_param import ResponseFormatTextConfigParam as ResponseFormatTextConfigParam +from .response_function_shell_tool_call import ResponseFunctionShellToolCall as ResponseFunctionShellToolCall from .response_function_tool_call_param import ResponseFunctionToolCallParam as ResponseFunctionToolCallParam from .response_input_file_content_param import ResponseInputFileContentParam as ResponseInputFileContentParam from .response_input_text_content_param import ResponseInputTextContentParam as ResponseInputTextContentParam @@ -125,6 +135,7 @@ from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam as ResponseFileSearchToolCallParam from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent as ResponseMcpListToolsFailedEvent +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput as ResponseApplyPatchToolCallOutput from .response_audio_transcript_delta_event import ( ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, ) @@ -158,6 +169,9 @@ from .response_function_call_output_item_param import ( ResponseFunctionCallOutputItemParam as ResponseFunctionCallOutputItemParam, ) +from .response_function_shell_tool_call_output import ( + ResponseFunctionShellToolCallOutput as ResponseFunctionShellToolCallOutput, +) from .response_image_gen_call_generating_event import ( ResponseImageGenCallGeneratingEvent as ResponseImageGenCallGeneratingEvent, ) @@ -206,6 +220,9 @@ from .response_function_call_arguments_done_event import ( ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, ) +from .response_function_shell_call_output_content import ( + ResponseFunctionShellCallOutputContent as ResponseFunctionShellCallOutputContent, +) from .response_image_gen_call_partial_image_event import ( ResponseImageGenCallPartialImageEvent as ResponseImageGenCallPartialImageEvent, ) @@ -245,6 +262,9 @@ from .response_code_interpreter_call_interpreting_event import ( ResponseCodeInterpreterCallInterpretingEvent as ResponseCodeInterpreterCallInterpretingEvent, ) +from .response_function_shell_call_output_content_param import ( + ResponseFunctionShellCallOutputContentParam as ResponseFunctionShellCallOutputContentParam, +) from .response_computer_tool_call_output_screenshot_param import ( ResponseComputerToolCallOutputScreenshotParam as ResponseComputerToolCallOutputScreenshotParam, ) diff --git a/src/openai/types/responses/apply_patch_tool.py b/src/openai/types/responses/apply_patch_tool.py new file mode 100644 index 0000000000..07706ce239 --- /dev/null +++ b/src/openai/types/responses/apply_patch_tool.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ApplyPatchTool"] + + +class ApplyPatchTool(BaseModel): + type: Literal["apply_patch"] + """The type of the tool. Always `apply_patch`.""" diff --git a/src/openai/types/responses/apply_patch_tool_param.py b/src/openai/types/responses/apply_patch_tool_param.py new file mode 100644 index 0000000000..93d15f0b1f --- /dev/null +++ b/src/openai/types/responses/apply_patch_tool_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ApplyPatchToolParam"] + + +class ApplyPatchToolParam(TypedDict, total=False): + type: Required[Literal["apply_patch"]] + """The type of the tool. Always `apply_patch`.""" diff --git a/src/openai/types/responses/function_shell_tool.py b/src/openai/types/responses/function_shell_tool.py new file mode 100644 index 0000000000..1784b6c2f1 --- /dev/null +++ b/src/openai/types/responses/function_shell_tool.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FunctionShellTool"] + + +class FunctionShellTool(BaseModel): + type: Literal["shell"] + """The type of the shell tool. Always `shell`.""" diff --git a/src/openai/types/responses/function_shell_tool_param.py b/src/openai/types/responses/function_shell_tool_param.py new file mode 100644 index 0000000000..cee7ba23c9 --- /dev/null +++ b/src/openai/types/responses/function_shell_tool_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["FunctionShellToolParam"] + + +class FunctionShellToolParam(TypedDict, total=False): + type: Required[Literal["shell"]] + """The type of the shell tool. Always `shell`.""" diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py index d442a2d171..296d0718d8 100644 --- a/src/openai/types/responses/input_token_count_params.py +++ b/src/openai/types/responses/input_token_count_params.py @@ -8,6 +8,7 @@ from .tool_param import ToolParam from .tool_choice_options import ToolChoiceOptions from .tool_choice_mcp_param import ToolChoiceMcpParam +from .tool_choice_shell_param import ToolChoiceShellParam from .tool_choice_types_param import ToolChoiceTypesParam from ..shared_params.reasoning import Reasoning from .tool_choice_custom_param import ToolChoiceCustomParam @@ -15,6 +16,7 @@ from .tool_choice_allowed_param import ToolChoiceAllowedParam from .tool_choice_function_param import ToolChoiceFunctionParam from .response_conversation_param import ResponseConversationParam +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam from .response_format_text_config_param import ResponseFormatTextConfigParam __all__ = ["InputTokenCountParams", "Conversation", "Text", "ToolChoice"] @@ -135,4 +137,6 @@ class Text(TypedDict, total=False): ToolChoiceFunctionParam, ToolChoiceMcpParam, ToolChoiceCustomParam, + ToolChoiceApplyPatchParam, + ToolChoiceShellParam, ] diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index 1d9db361dd..c120f4641d 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -23,8 +23,12 @@ from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput __all__ = ["ParsedResponse", "ParsedResponseOutputMessage", "ParsedResponseOutputText"] @@ -75,6 +79,10 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): McpListTools, ResponseCodeInterpreterToolCall, ResponseCustomToolCall, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index a1133a41f5..cdd143f1cb 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -12,6 +12,7 @@ from .tool_choice_mcp import ToolChoiceMcp from ..shared.metadata import Metadata from ..shared.reasoning import Reasoning +from .tool_choice_shell import ToolChoiceShell from .tool_choice_types import ToolChoiceTypes from .tool_choice_custom import ToolChoiceCustom from .response_input_item import ResponseInputItem @@ -21,6 +22,7 @@ from .response_text_config import ResponseTextConfig from .tool_choice_function import ToolChoiceFunction from ..shared.responses_model import ResponsesModel +from .tool_choice_apply_patch import ToolChoiceApplyPatch __all__ = ["Response", "IncompleteDetails", "ToolChoice", "Conversation"] @@ -31,7 +33,14 @@ class IncompleteDetails(BaseModel): ToolChoice: TypeAlias = Union[ - ToolChoiceOptions, ToolChoiceAllowed, ToolChoiceTypes, ToolChoiceFunction, ToolChoiceMcp, ToolChoiceCustom + ToolChoiceOptions, + ToolChoiceAllowed, + ToolChoiceTypes, + ToolChoiceFunction, + ToolChoiceMcp, + ToolChoiceCustom, + ToolChoiceApplyPatch, + ToolChoiceShell, ] @@ -192,6 +201,14 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] = None + """The retention policy for the prompt cache. + + Set to `24h` to enable extended prompt caching, which keeps cached prefixes + active for longer, up to a maximum of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + """ + reasoning: Optional[Reasoning] = None """**gpt-5 and o-series models only** diff --git a/src/openai/types/responses/response_apply_patch_tool_call.py b/src/openai/types/responses/response_apply_patch_tool_call.py new file mode 100644 index 0000000000..78eb8c8339 --- /dev/null +++ b/src/openai/types/responses/response_apply_patch_tool_call.py @@ -0,0 +1,76 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseApplyPatchToolCall", + "Operation", + "OperationCreateFile", + "OperationDeleteFile", + "OperationUpdateFile", +] + + +class OperationCreateFile(BaseModel): + diff: str + """Diff to apply.""" + + path: str + """Path of the file to create.""" + + type: Literal["create_file"] + """Create a new file with the provided diff.""" + + +class OperationDeleteFile(BaseModel): + path: str + """Path of the file to delete.""" + + type: Literal["delete_file"] + """Delete the specified file.""" + + +class OperationUpdateFile(BaseModel): + diff: str + """Diff to apply.""" + + path: str + """Path of the file to update.""" + + type: Literal["update_file"] + """Update an existing file with the provided diff.""" + + +Operation: TypeAlias = Annotated[ + Union[OperationCreateFile, OperationDeleteFile, OperationUpdateFile], PropertyInfo(discriminator="type") +] + + +class ResponseApplyPatchToolCall(BaseModel): + id: str + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" + + operation: Optional[Operation] = None + """ + One of the create_file, delete_file, or update_file operations applied via + apply_patch. + """ diff --git a/src/openai/types/responses/response_apply_patch_tool_call_output.py b/src/openai/types/responses/response_apply_patch_tool_call_output.py new file mode 100644 index 0000000000..7aee5fae9c --- /dev/null +++ b/src/openai/types/responses/response_apply_patch_tool_call_output.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseApplyPatchToolCallOutput"] + + +class ResponseApplyPatchToolCallOutput(BaseModel): + id: str + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + output: Optional[str] = None + """Optional textual output returned by the apply patch tool.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call output.""" diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index ba5c45ffee..64888ac62d 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -12,6 +12,7 @@ from .response_prompt_param import ResponsePromptParam from .tool_choice_mcp_param import ToolChoiceMcpParam from ..shared_params.metadata import Metadata +from .tool_choice_shell_param import ToolChoiceShellParam from .tool_choice_types_param import ToolChoiceTypesParam from ..shared_params.reasoning import Reasoning from .tool_choice_custom_param import ToolChoiceCustomParam @@ -19,6 +20,7 @@ from .response_text_config_param import ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam from .response_conversation_param import ResponseConversationParam +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam from ..shared_params.responses_model import ResponsesModel __all__ = [ @@ -146,6 +148,14 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] + """The retention policy for the prompt cache. + + Set to `24h` to enable extended prompt caching, which keeps cached prefixes + active for longer, up to a maximum of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + """ + reasoning: Optional[Reasoning] """**gpt-5 and o-series models only** @@ -292,6 +302,8 @@ class StreamOptions(TypedDict, total=False): ToolChoiceFunctionParam, ToolChoiceMcpParam, ToolChoiceCustomParam, + ToolChoiceApplyPatchParam, + ToolChoiceShellParam, ] diff --git a/src/openai/types/responses/response_function_shell_call_output_content.py b/src/openai/types/responses/response_function_shell_call_output_content.py new file mode 100644 index 0000000000..1429ce9724 --- /dev/null +++ b/src/openai/types/responses/response_function_shell_call_output_content.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["ResponseFunctionShellCallOutputContent", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(BaseModel): + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(BaseModel): + exit_code: int + """The exit code returned by the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Annotated[Union[OutcomeTimeout, OutcomeExit], PropertyInfo(discriminator="type")] + + +class ResponseFunctionShellCallOutputContent(BaseModel): + outcome: Outcome + """The exit or timeout outcome associated with this chunk.""" + + stderr: str + """Captured stderr output for this chunk of the shell call.""" + + stdout: str + """Captured stdout output for this chunk of the shell call.""" diff --git a/src/openai/types/responses/response_function_shell_call_output_content_param.py b/src/openai/types/responses/response_function_shell_call_output_content_param.py new file mode 100644 index 0000000000..6395541cf5 --- /dev/null +++ b/src/openai/types/responses/response_function_shell_call_output_content_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["ResponseFunctionShellCallOutputContentParam", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(TypedDict, total=False): + type: Required[Literal["timeout"]] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(TypedDict, total=False): + exit_code: Required[int] + """The exit code returned by the shell process.""" + + type: Required[Literal["exit"]] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Union[OutcomeTimeout, OutcomeExit] + + +class ResponseFunctionShellCallOutputContentParam(TypedDict, total=False): + outcome: Required[Outcome] + """The exit or timeout outcome associated with this chunk.""" + + stderr: Required[str] + """Captured stderr output for this chunk of the shell call.""" + + stdout: Required[str] + """Captured stdout output for this chunk of the shell call.""" diff --git a/src/openai/types/responses/response_function_shell_tool_call.py b/src/openai/types/responses/response_function_shell_tool_call.py new file mode 100644 index 0000000000..be0a5bcff8 --- /dev/null +++ b/src/openai/types/responses/response_function_shell_tool_call.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseFunctionShellToolCall", "Action"] + + +class Action(BaseModel): + commands: List[str] + + max_output_length: Optional[int] = None + """Optional maximum number of characters to return from each command.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the commands.""" + + +class ResponseFunctionShellToolCall(BaseModel): + id: str + """The unique ID of the function shell tool call. + + Populated when this item is returned via API. + """ + + action: Action + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the function shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + type: Literal["shell_call"] + """The type of the item. Always `shell_call`.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" diff --git a/src/openai/types/responses/response_function_shell_tool_call_output.py b/src/openai/types/responses/response_function_shell_tool_call_output.py new file mode 100644 index 0000000000..e74927df41 --- /dev/null +++ b/src/openai/types/responses/response_function_shell_tool_call_output.py @@ -0,0 +1,70 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ResponseFunctionShellToolCallOutput", + "Output", + "OutputOutcome", + "OutputOutcomeTimeout", + "OutputOutcomeExit", +] + + +class OutputOutcomeTimeout(BaseModel): + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutputOutcomeExit(BaseModel): + exit_code: int + """Exit code from the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")] + + +class Output(BaseModel): + outcome: OutputOutcome + """ + Represents either an exit outcome (with an exit code) or a timeout outcome for a + shell call output chunk. + """ + + stderr: str + + stdout: str + + created_by: Optional[str] = None + + +class ResponseFunctionShellToolCallOutput(BaseModel): + id: str + """The unique ID of the shell call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + max_output_length: Optional[int] = None + """The maximum length of the shell command output. + + This is generated by the model and should be passed back with the raw output. + """ + + output: List[Output] + """An array of shell call output contents""" + + type: Literal["shell_call_output"] + """The type of the shell call output. Always `shell_call_output`.""" + + created_by: Optional[str] = None diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index 0a487b8bef..eaf5396087 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -17,6 +17,7 @@ from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_input_message_content_list import ResponseInputMessageContentList from .response_function_call_output_item_list import ResponseFunctionCallOutputItemList +from .response_function_shell_call_output_content import ResponseFunctionShellCallOutputContent from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot __all__ = [ @@ -29,6 +30,15 @@ "LocalShellCall", "LocalShellCallAction", "LocalShellCallOutput", + "ShellCall", + "ShellCallAction", + "ShellCallOutput", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallOutput", "McpListTools", "McpListToolsTool", "McpApprovalRequest", @@ -186,6 +196,151 @@ class LocalShellCallOutput(BaseModel): """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" +class ShellCallAction(BaseModel): + commands: List[str] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] = None + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] = None + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCall(BaseModel): + action: ShellCallAction + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the function shell tool call generated by the model.""" + + type: Literal["shell_call"] + """The type of the item. Always `function_shell_call`.""" + + id: Optional[str] = None + """The unique ID of the function shell tool call. + + Populated when this item is returned via API. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutput(BaseModel): + call_id: str + """The unique ID of the function shell tool call generated by the model.""" + + output: List[ResponseFunctionShellCallOutputContent] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Literal["shell_call_output"] + """The type of the item. Always `function_shell_call_output`.""" + + id: Optional[str] = None + """The unique ID of the function shell tool call output. + + Populated when this item is returned via API. + """ + + max_output_length: Optional[int] = None + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + +class ApplyPatchCallOperationCreateFile(BaseModel): + diff: str + """Unified diff content to apply when creating the file.""" + + path: str + """Path of the file to create relative to the workspace root.""" + + type: Literal["create_file"] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(BaseModel): + path: str + """Path of the file to delete relative to the workspace root.""" + + type: Literal["delete_file"] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(BaseModel): + diff: str + """Unified diff content to apply to the existing file.""" + + path: str + """Path of the file to update relative to the workspace root.""" + + type: Literal["update_file"] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Annotated[ + Union[ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile], + PropertyInfo(discriminator="type"), +] + + +class ApplyPatchCall(BaseModel): + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + operation: ApplyPatchCallOperation + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + +class ApplyPatchCallOutput(BaseModel): + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + output: Optional[str] = None + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + class McpListToolsTool(BaseModel): input_schema: object """The JSON schema describing the tool's input.""" @@ -311,6 +466,10 @@ class ItemReference(BaseModel): ResponseCodeInterpreterToolCall, LocalShellCall, LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, McpListTools, McpApprovalRequest, McpApprovalResponse, diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 115147dc4b..231063cb5e 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -18,6 +18,7 @@ from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam +from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam __all__ = [ @@ -30,6 +31,15 @@ "LocalShellCall", "LocalShellCallAction", "LocalShellCallOutput", + "ShellCall", + "ShellCallAction", + "ShellCallOutput", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallOutput", "McpListTools", "McpListToolsTool", "McpApprovalRequest", @@ -187,6 +197,150 @@ class LocalShellCallOutput(TypedDict, total=False): """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" +class ShellCallAction(TypedDict, total=False): + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCall(TypedDict, total=False): + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the function shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `function_shell_call`.""" + + id: Optional[str] + """The unique ID of the function shell tool call. + + Populated when this item is returned via API. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutput(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the function shell tool call generated by the model.""" + + output: Required[Iterable[ResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `function_shell_call_output`.""" + + id: Optional[str] + """The unique ID of the function shell tool call output. + + Populated when this item is returned via API. + """ + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCall(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + +class ApplyPatchCallOutput(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + output: str + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + class McpListToolsTool(TypedDict, total=False): input_schema: Required[object] """The JSON schema describing the tool's input.""" @@ -311,6 +465,10 @@ class ItemReference(TypedDict, total=False): ResponseCodeInterpreterToolCallParam, LocalShellCall, LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, McpListTools, McpApprovalRequest, McpApprovalResponse, diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 9a999c7252..15d9480eaf 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -18,6 +18,7 @@ from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam +from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam __all__ = [ @@ -31,6 +32,15 @@ "LocalShellCall", "LocalShellCallAction", "LocalShellCallOutput", + "ShellCall", + "ShellCallAction", + "ShellCallOutput", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallOutput", "McpListTools", "McpListToolsTool", "McpApprovalRequest", @@ -188,6 +198,150 @@ class LocalShellCallOutput(TypedDict, total=False): """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" +class ShellCallAction(TypedDict, total=False): + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCall(TypedDict, total=False): + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the function shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `function_shell_call`.""" + + id: Optional[str] + """The unique ID of the function shell tool call. + + Populated when this item is returned via API. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutput(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the function shell tool call generated by the model.""" + + output: Required[Iterable[ResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `function_shell_call_output`.""" + + id: Optional[str] + """The unique ID of the function shell tool call output. + + Populated when this item is returned via API. + """ + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCall(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + +class ApplyPatchCallOutput(TypedDict, total=False): + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + output: str + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + class McpListToolsTool(TypedDict, total=False): input_schema: Required[object] """The JSON schema describing the tool's input.""" @@ -312,6 +466,10 @@ class ItemReference(TypedDict, total=False): ResponseCodeInterpreterToolCallParam, LocalShellCall, LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, McpListTools, McpApprovalRequest, McpApprovalResponse, diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py index bdd2523baf..5ae2405988 100644 --- a/src/openai/types/responses/response_item.py +++ b/src/openai/types/responses/response_item.py @@ -9,11 +9,15 @@ from .response_computer_tool_call import ResponseComputerToolCall from .response_input_message_item import ResponseInputMessageItem from .response_function_web_search import ResponseFunctionWebSearch +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall from .response_file_search_tool_call import ResponseFileSearchToolCall from .response_function_tool_call_item import ResponseFunctionToolCallItem +from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem +from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput __all__ = [ "ResponseItem", @@ -209,6 +213,10 @@ class McpCall(BaseModel): ResponseCodeInterpreterToolCall, LocalShellCall, LocalShellCallOutput, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, McpListTools, McpApprovalRequest, McpApprovalResponse, diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index e33d59cefe..906ddbb25e 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -11,8 +11,12 @@ from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch +from .response_apply_patch_tool_call import ResponseApplyPatchToolCall from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall +from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput __all__ = [ "ResponseOutputItem", @@ -172,6 +176,10 @@ class McpApprovalRequest(BaseModel): ImageGenerationCall, ResponseCodeInterpreterToolCall, LocalShellCall, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, McpCall, McpListTools, McpApprovalRequest, diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index b29fede0c9..ae8b34b1f4 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -10,7 +10,9 @@ from .computer_tool import ComputerTool from .function_tool import FunctionTool from .web_search_tool import WebSearchTool +from .apply_patch_tool import ApplyPatchTool from .file_search_tool import FileSearchTool +from .function_shell_tool import FunctionShellTool from .web_search_preview_tool import WebSearchPreviewTool __all__ = [ @@ -260,8 +262,10 @@ class LocalShell(BaseModel): CodeInterpreter, ImageGeneration, LocalShell, + FunctionShellTool, CustomTool, WebSearchPreviewTool, + ApplyPatchTool, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/tool_choice_apply_patch.py b/src/openai/types/responses/tool_choice_apply_patch.py new file mode 100644 index 0000000000..7f815aa1a1 --- /dev/null +++ b/src/openai/types/responses/tool_choice_apply_patch.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceApplyPatch"] + + +class ToolChoiceApplyPatch(BaseModel): + type: Literal["apply_patch"] + """The tool to call. Always `apply_patch`.""" diff --git a/src/openai/types/responses/tool_choice_apply_patch_param.py b/src/openai/types/responses/tool_choice_apply_patch_param.py new file mode 100644 index 0000000000..00d4b25f0e --- /dev/null +++ b/src/openai/types/responses/tool_choice_apply_patch_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceApplyPatchParam"] + + +class ToolChoiceApplyPatchParam(TypedDict, total=False): + type: Required[Literal["apply_patch"]] + """The tool to call. Always `apply_patch`.""" diff --git a/src/openai/types/responses/tool_choice_shell.py b/src/openai/types/responses/tool_choice_shell.py new file mode 100644 index 0000000000..1ad21c58f3 --- /dev/null +++ b/src/openai/types/responses/tool_choice_shell.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolChoiceShell"] + + +class ToolChoiceShell(BaseModel): + type: Literal["shell"] + """The tool to call. Always `shell`.""" diff --git a/src/openai/types/responses/tool_choice_shell_param.py b/src/openai/types/responses/tool_choice_shell_param.py new file mode 100644 index 0000000000..2b04c00d56 --- /dev/null +++ b/src/openai/types/responses/tool_choice_shell_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolChoiceShellParam"] + + +class ToolChoiceShellParam(TypedDict, total=False): + type: Required[Literal["shell"]] + """The tool to call. Always `shell`.""" diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index dd1ea0bd54..18b044ab8c 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -12,7 +12,9 @@ from .computer_tool_param import ComputerToolParam from .function_tool_param import FunctionToolParam from .web_search_tool_param import WebSearchToolParam +from .apply_patch_tool_param import ApplyPatchToolParam from .file_search_tool_param import FileSearchToolParam +from .function_shell_tool_param import FunctionShellToolParam from .web_search_preview_tool_param import WebSearchPreviewToolParam __all__ = [ @@ -259,8 +261,10 @@ class LocalShell(TypedDict, total=False): CodeInterpreter, ImageGeneration, LocalShell, + FunctionShellToolParam, CustomToolParam, WebSearchPreviewToolParam, + ApplyPatchToolParam, ] diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index 727c60c1c0..b3ae7c3a95 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -5,6 +5,11 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", "gpt-5", "gpt-5-mini", "gpt-5-nano", diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 6ea2fe82bf..cf470ca057 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -14,12 +14,16 @@ class Reasoning(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None diff --git a/src/openai/types/shared/reasoning_effort.py b/src/openai/types/shared/reasoning_effort.py index 4b960cd7e6..c890a133cc 100644 --- a/src/openai/types/shared/reasoning_effort.py +++ b/src/openai/types/shared/reasoning_effort.py @@ -5,4 +5,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["minimal", "low", "medium", "high"]] +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high"]] diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index a1e5ab9f30..7505d51d67 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -7,6 +7,11 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", "gpt-5", "gpt-5-mini", "gpt-5-nano", diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 5c1eff683f..ad58f70b71 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -15,12 +15,16 @@ class Reasoning(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `minimal`, `low`, `medium`, and `high`. Reducing reasoning - effort can result in faster responses and fewer tokens used on reasoning in a - response. - - Note: The `gpt-5-pro` model defaults to (and only supports) `high` reasoning - effort. + supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. + + - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported + reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool + calls are supported for all reasoning values in gpt-5.1. + - All models before `gpt-5.1` default to `medium` reasoning effort, and do not + support `none`. + - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] diff --git a/src/openai/types/shared_params/reasoning_effort.py b/src/openai/types/shared_params/reasoning_effort.py index 4c095a28d7..e388eebff1 100644 --- a/src/openai/types/shared_params/reasoning_effort.py +++ b/src/openai/types/shared_params/reasoning_effort.py @@ -7,4 +7,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["minimal", "low", "medium", "high"]] +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high"]] diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 875e024a51..2557735426 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -36,7 +36,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: instructions="instructions", metadata={"foo": "string"}, name="name", - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", temperature=1, tool_resources={ @@ -135,7 +135,7 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="string", name="name", - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", temperature=1, tool_resources={ @@ -272,7 +272,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> instructions="instructions", metadata={"foo": "string"}, name="name", - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", temperature=1, tool_resources={ @@ -371,7 +371,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> metadata={"foo": "string"}, model="string", name="name", - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", temperature=1, tool_resources={ diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 440486bac5..3a6b36864d 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -59,7 +59,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", stream=False, temperature=1, @@ -150,7 +150,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", temperature=1, tool_choice="none", @@ -609,7 +609,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", stream=False, temperature=1, @@ -700,7 +700,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn metadata={"foo": "string"}, model="string", parallel_tool_calls=True, - reasoning_effort="minimal", + reasoning_effort="none", response_format="auto", temperature=1, tool_choice="none", diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 358ea18cbb..2b58ff8191 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -73,7 +73,8 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="minimal", + prompt_cache_retention="in-memory", + reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, @@ -206,7 +207,8 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="minimal", + prompt_cache_retention="in-memory", + reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, @@ -514,7 +516,8 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="minimal", + prompt_cache_retention="in-memory", + reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, @@ -647,7 +650,8 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - reasoning_effort="minimal", + prompt_cache_retention="in-memory", + reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", seed=-9007199254740991, diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py index 54ba2d25c2..d9aecc33bd 100644 --- a/tests/api_resources/responses/test_input_tokens.py +++ b/tests/api_resources/responses/test_input_tokens.py @@ -32,7 +32,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None: parallel_tool_calls=True, previous_response_id="resp_123", reasoning={ - "effort": "minimal", + "effort": "none", "generate_summary": "auto", "summary": "auto", }, @@ -95,7 +95,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> parallel_tool_calls=True, previous_response_id="resp_123", reasoning={ - "effort": "minimal", + "effort": "none", "generate_summary": "auto", "summary": "auto", }, diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index a329aa4d9e..b57e6099c4 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -45,8 +45,9 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_retention="in-memory", reasoning={ - "effort": "minimal", + "effort": "none", "generate_summary": "auto", "summary": "auto", }, @@ -125,8 +126,9 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_retention="in-memory", reasoning={ - "effort": "minimal", + "effort": "none", "generate_summary": "auto", "summary": "auto", }, @@ -398,8 +400,9 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_retention="in-memory", reasoning={ - "effort": "minimal", + "effort": "none", "generate_summary": "auto", "summary": "auto", }, @@ -478,8 +481,9 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_retention="in-memory", reasoning={ - "effort": "minimal", + "effort": "none", "generate_summary": "auto", "summary": "auto", }, From 9b8c7e3e84e3e7bb8b0d88bb7d0538c39bb47a40 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:11:54 +0000 Subject: [PATCH 166/408] release: 2.8.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e3ec769daf..64f9ff4148 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.7.2" + ".": "2.8.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 03dfa94242..5d49a82e5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.8.0 (2025-11-13) + +Full Changelog: [v2.7.2...v2.8.0](https://github.com/openai/openai-python/compare/v2.7.2...v2.8.0) + +### Features + +* **api:** gpt 5.1 ([8d9f2ca](https://github.com/openai/openai-python/commit/8d9f2cab4cb2e12f6e2ab1de967f858736a656ac)) + + +### Bug Fixes + +* **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([c7bd234](https://github.com/openai/openai-python/commit/c7bd234b18239fcdbf0edb1b51ca9116c0ac7251)) + ## 2.7.2 (2025-11-10) Full Changelog: [v2.7.1...v2.7.2](https://github.com/openai/openai-python/compare/v2.7.1...v2.7.2) diff --git a/pyproject.toml b/pyproject.toml index ca13765a98..0c4d4b62a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.7.2" +version = "2.8.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6c0fcb3469..63e309efdd 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.7.2" # x-release-please-version +__version__ = "2.8.0" # x-release-please-version From f0f0ccce35d0f7eccca7f85539a5a23db1e4ec1c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 21:19:50 +0000 Subject: [PATCH 167/408] fix(api): align types of input items / output items for typescript --- .stats.yml | 4 ++-- .../responses/response_apply_patch_tool_call.py | 12 ++++++------ .../response_apply_patch_tool_call_output.py | 6 +++--- .../types/responses/response_input_item_param.py | 2 +- src/openai/types/responses/response_input_param.py | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.stats.yml b/.stats.yml index b44bda286e..fe1a09be6b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-ca24bc4d8125b5153514ce643c4e3220f25971b7d67ca384d56d493c72c0d977.yml -openapi_spec_hash: c6f048c7b3d29f4de48fde0e845ba33f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a7e92d12ebe89ca019a7ac5b29759064eefa2c38fe08d03516f2620e66abb32b.yml +openapi_spec_hash: acbc703b2739447abc6312b2d753631c config_hash: b876221dfb213df9f0a999e75d38a65e diff --git a/src/openai/types/responses/response_apply_patch_tool_call.py b/src/openai/types/responses/response_apply_patch_tool_call.py index 78eb8c8339..7dc2a3c2b5 100644 --- a/src/openai/types/responses/response_apply_patch_tool_call.py +++ b/src/openai/types/responses/response_apply_patch_tool_call.py @@ -60,6 +60,12 @@ class ResponseApplyPatchToolCall(BaseModel): call_id: str """The unique ID of the apply patch tool call generated by the model.""" + operation: Operation + """ + One of the create_file, delete_file, or update_file operations applied via + apply_patch. + """ + status: Literal["in_progress", "completed"] """The status of the apply patch tool call. One of `in_progress` or `completed`.""" @@ -68,9 +74,3 @@ class ResponseApplyPatchToolCall(BaseModel): created_by: Optional[str] = None """The ID of the entity that created this tool call.""" - - operation: Optional[Operation] = None - """ - One of the create_file, delete_file, or update_file operations applied via - apply_patch. - """ diff --git a/src/openai/types/responses/response_apply_patch_tool_call_output.py b/src/openai/types/responses/response_apply_patch_tool_call_output.py index 7aee5fae9c..cf0bcfeecc 100644 --- a/src/openai/types/responses/response_apply_patch_tool_call_output.py +++ b/src/openai/types/responses/response_apply_patch_tool_call_output.py @@ -18,9 +18,6 @@ class ResponseApplyPatchToolCallOutput(BaseModel): call_id: str """The unique ID of the apply patch tool call generated by the model.""" - output: Optional[str] = None - """Optional textual output returned by the apply patch tool.""" - status: Literal["completed", "failed"] """The status of the apply patch tool call output. One of `completed` or `failed`.""" @@ -29,3 +26,6 @@ class ResponseApplyPatchToolCallOutput(BaseModel): created_by: Optional[str] = None """The ID of the entity that created this tool call output.""" + + output: Optional[str] = None + """Optional textual output returned by the apply patch tool.""" diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 231063cb5e..5c2e81c4de 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -334,7 +334,7 @@ class ApplyPatchCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ - output: str + output: Optional[str] """ Optional human-readable log text from the apply patch tool (e.g., patch results or errors). diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 15d9480eaf..365c6b3d7b 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -335,7 +335,7 @@ class ApplyPatchCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ - output: str + output: Optional[str] """ Optional human-readable log text from the apply patch tool (e.g., patch results or errors). From 41ee03ffe985a7362f0275c7f500080cb1d58cdd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 21:20:21 +0000 Subject: [PATCH 168/408] release: 2.8.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 64f9ff4148..108509ed29 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.8.0" + ".": "2.8.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d49a82e5c..1bfa59348f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.8.1 (2025-11-17) + +Full Changelog: [v2.8.0...v2.8.1](https://github.com/openai/openai-python/compare/v2.8.0...v2.8.1) + +### Bug Fixes + +* **api:** align types of input items / output items for typescript ([64c9fb3](https://github.com/openai/openai-python/commit/64c9fb3fcc79f0049b3a36bd429faf0600d969f6)) + ## 2.8.0 (2025-11-13) Full Changelog: [v2.7.2...v2.8.0](https://github.com/openai/openai-python/compare/v2.7.2...v2.8.0) diff --git a/pyproject.toml b/pyproject.toml index 0c4d4b62a7..75118d46be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.8.0" +version = "2.8.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 63e309efdd..6109cebf91 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.8.0" # x-release-please-version +__version__ = "2.8.1" # x-release-please-version From df4109a62b95c8e7ff5ab8d9d03294c738a4ccc0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 23:26:30 +0000 Subject: [PATCH 169/408] chore(internal): codegen related update --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 75118d46be..7a05625aa6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", From fc4825045e8c95cdd73b5acff6d7b7db7abd19f8 Mon Sep 17 00:00:00 2001 From: peter-zhong-replit Date: Mon, 1 Dec 2025 05:13:27 -0800 Subject: [PATCH 170/408] fix(client): avoid mutating user-provided response config object (#2700) --- src/openai/resources/responses/responses.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index dcf87ba07c..8365b808ae 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -2,6 +2,7 @@ from __future__ import annotations +from copy import copy from typing import Any, List, Type, Union, Iterable, Optional, cast from functools import partial from typing_extensions import Literal, overload @@ -1046,6 +1047,7 @@ def stream( if "format" in text: raise TypeError("Cannot mix and match text.format with text_format") + text = copy(text) text["format"] = _type_to_text_format_param(text_format) api_request: partial[Stream[ResponseStreamEvent]] = partial( @@ -1151,7 +1153,7 @@ def parse( if "format" in text: raise TypeError("Cannot mix and match text.format with text_format") - + text = copy(text) text["format"] = _type_to_text_format_param(text_format) tools = _make_tools(tools) @@ -2507,7 +2509,7 @@ def stream( if "format" in text: raise TypeError("Cannot mix and match text.format with text_format") - + text = copy(text) text["format"] = _type_to_text_format_param(text_format) api_request = self.create( @@ -2617,7 +2619,7 @@ async def parse( if "format" in text: raise TypeError("Cannot mix and match text.format with text_format") - + text = copy(text) text["format"] = _type_to_text_format_param(text_format) tools = _make_tools(tools) From f6552d762e3aec145c49428913a1b9333e4b6be3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:56:43 +0000 Subject: [PATCH 171/408] fix: ensure streams are always closed --- src/openai/_streaming.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 05c284a2be..74e54f74fa 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -55,9 +55,10 @@ def __stream__(self) -> Iterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - for sse in iterator: - if sse.data.startswith("[DONE]"): - break + try: + for sse in iterator: + if sse.data.startswith("[DONE]"): + break # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data if sse.event and sse.event.startswith("thread."): @@ -96,8 +97,9 @@ def __stream__(self) -> Iterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) - # As we might not fully consume the response stream, we need to close it explicitly - response.close() + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() def __enter__(self) -> Self: return self @@ -156,9 +158,10 @@ async def __stream__(self) -> AsyncIterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - async for sse in iterator: - if sse.data.startswith("[DONE]"): - break + try: + async for sse in iterator: + if sse.data.startswith("[DONE]"): + break # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data if sse.event and sse.event.startswith("thread."): @@ -197,8 +200,9 @@ async def __stream__(self) -> AsyncIterator[_T]: yield process_data(data=data, cast_to=cast_to, response=response) - # As we might not fully consume the response stream, we need to close it explicitly - await response.aclose() + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() async def __aenter__(self) -> Self: return self From 1e7eae9e766c7479507469d7ebaabba13a4fcad5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:01:53 +0000 Subject: [PATCH 172/408] chore(deps): mypy 1.18.1 has a regression, pin to 1.17 --- pyproject.toml | 2 +- requirements-dev.lock | 4 +++- requirements.lock | 9 +++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7a05625aa6..4130dba20a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ managed = true # version pins are in requirements-dev.lock dev-dependencies = [ "pyright==1.1.399", - "mypy", + "mypy==1.17", "respx", "pytest", "pytest-asyncio", diff --git a/requirements-dev.lock b/requirements-dev.lock index b454537b96..07443f2dae 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -105,7 +105,7 @@ msal-extensions==1.2.0 multidict==6.5.0 # via aiohttp # via yarl -mypy==1.14.1 +mypy==1.17.0 mypy-extensions==1.0.0 # via mypy nest-asyncio==1.6.0 @@ -125,6 +125,8 @@ pandas==2.2.3 # via openai pandas-stubs==2.1.4.231227 # via openai +pathspec==0.12.1 + # via mypy platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 diff --git a/requirements.lock b/requirements.lock index b047cb3f88..386ec3c590 100644 --- a/requirements.lock +++ b/requirements.lock @@ -69,9 +69,9 @@ propcache==0.3.2 # via yarl pycparser==2.23 # via cffi -pydantic==2.11.9 +pydantic==2.12.5 # via openai -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic python-dateutil==2.9.0.post0 # via pandas @@ -88,13 +88,14 @@ tqdm==4.66.5 # via openai types-pytz==2024.2.0.20241003 # via pandas-stubs -typing-extensions==4.12.2 +typing-extensions==4.15.0 + # via anyio # via multidict # via openai # via pydantic # via pydantic-core # via typing-inspection -typing-inspection==0.4.1 +typing-inspection==0.4.2 # via pydantic tzdata==2025.2 # via pandas From c3c607a2e2abc572bde6220e71921bf8a55949ab Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 11:05:28 +0000 Subject: [PATCH 173/408] chore: update lockfile --- pyproject.toml | 12 +-- requirements-dev.lock | 165 ++++++++++++++++++++++-------------------- requirements.lock | 47 ++++++------ 3 files changed, 119 insertions(+), 105 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4130dba20a..3ad3b4a58a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,16 +7,18 @@ license = "Apache-2.0" authors = [ { name = "OpenAI", email = "support@openai.com" }, ] + dependencies = [ - "httpx>=0.23.0, <1", - "pydantic>=1.9.0, <3", + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", "typing-extensions>=4.11, <5", - "anyio>=3.5.0, <5", - "distro>=1.7.0, <2", - "sniffio", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", "tqdm > 4", "jiter>=0.10.0, <1", ] + requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", diff --git a/requirements-dev.lock b/requirements-dev.lock index 07443f2dae..a7201a127b 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,65 +12,70 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.13 +aiohttp==3.13.2 # via httpx-aiohttp # via openai -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.1.0 +anyio==4.12.0 # via httpx # via openai -argcomplete==3.1.2 +argcomplete==3.6.3 # via nox -asttokens==2.4.1 +asttokens==3.0.1 # via inline-snapshot async-timeout==5.0.1 # via aiohttp -attrs==24.2.0 +attrs==25.4.0 # via aiohttp + # via nox # via outcome # via trio -azure-core==1.31.0 +azure-core==1.36.0 # via azure-identity -azure-identity==1.19.0 -certifi==2023.7.22 +azure-identity==1.25.1 +backports-asyncio-runner==1.2.0 + # via pytest-asyncio +certifi==2025.11.12 # via httpcore # via httpx # via requests -cffi==1.16.0 +cffi==2.0.0 # via cryptography # via sounddevice -charset-normalizer==3.3.2 +charset-normalizer==3.4.4 # via requests colorama==0.4.6 # via griffe -colorlog==6.7.0 +colorlog==6.10.1 # via nox -cryptography==42.0.7 +cryptography==46.0.3 # via azure-identity # via msal # via pyjwt -dirty-equals==0.6.0 -distlib==0.3.7 +dependency-groups==1.3.1 + # via nox +dirty-equals==0.11 +distlib==0.4.0 # via virtualenv -distro==1.8.0 +distro==1.9.0 # via openai -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio # via pytest # via trio -execnet==2.1.1 +execnet==2.1.2 # via pytest-xdist -executing==2.2.0 +executing==2.2.1 # via inline-snapshot -filelock==3.12.4 +filelock==3.19.1 # via virtualenv -frozenlist==1.7.0 +frozenlist==1.8.0 # via aiohttp # via aiosignal -griffe==1.13.0 +griffe==1.14.0 h11==0.16.0 # via httpcore httpcore==1.0.9 @@ -81,139 +86,145 @@ httpx==0.28.1 # via respx httpx-aiohttp==0.1.9 # via openai -idna==3.4 +humanize==4.13.0 + # via nox +idna==3.11 # via anyio # via httpx # via requests # via trio # via yarl -importlib-metadata==7.0.0 -iniconfig==2.0.0 +importlib-metadata==8.7.0 +iniconfig==2.1.0 # via pytest -inline-snapshot==0.28.0 -jiter==0.11.0 +inline-snapshot==0.31.1 +jiter==0.12.0 # via openai markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -msal==1.31.0 +msal==1.34.0 # via azure-identity # via msal-extensions -msal-extensions==1.2.0 +msal-extensions==1.3.1 # via azure-identity -multidict==6.5.0 +multidict==6.7.0 # via aiohttp # via yarl mypy==1.17.0 -mypy-extensions==1.0.0 +mypy-extensions==1.1.0 # via mypy nest-asyncio==1.6.0 -nodeenv==1.8.0 +nodeenv==1.9.1 # via pyright -nox==2023.4.22 +nox==2025.11.12 numpy==2.0.2 # via openai # via pandas # via pandas-stubs outcome==1.3.0.post0 # via trio -packaging==23.2 +packaging==25.0 + # via dependency-groups # via nox # via pytest -pandas==2.2.3 +pandas==2.3.3 # via openai -pandas-stubs==2.1.4.231227 +pandas-stubs==2.2.2.240807 # via openai pathspec==0.12.1 # via mypy -platformdirs==3.11.0 +platformdirs==4.4.0 # via virtualenv -pluggy==1.5.0 +pluggy==1.6.0 # via pytest -portalocker==2.10.1 - # via msal-extensions -propcache==0.3.2 +propcache==0.4.1 # via aiohttp # via yarl pycparser==2.23 # via cffi -pydantic==2.11.9 +pydantic==2.12.5 # via openai -pydantic-core==2.33.2 +pydantic-core==2.41.5 # via pydantic -pygments==2.18.0 +pygments==2.19.2 # via pytest # via rich -pyjwt==2.8.0 +pyjwt==2.10.1 # via msal pyright==1.1.399 -pytest==8.4.1 +pytest==8.4.2 # via inline-snapshot # via pytest-asyncio # via pytest-xdist -pytest-asyncio==0.24.0 -pytest-xdist==3.7.0 -python-dateutil==2.8.2 +pytest-asyncio==1.2.0 +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 # via pandas # via time-machine -pytz==2023.3.post1 - # via dirty-equals +pytz==2025.2 # via pandas -requests==2.31.0 +requests==2.32.5 # via azure-core # via msal respx==0.22.0 -rich==13.7.1 +rich==14.2.0 # via inline-snapshot -ruff==0.9.4 -setuptools==68.2.2 - # via nodeenv -six==1.16.0 - # via asttokens - # via azure-core +ruff==0.14.7 +six==1.17.0 # via python-dateutil -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via openai # via trio sortedcontainers==2.4.0 # via trio -sounddevice==0.5.1 +sounddevice==0.5.3 # via openai -time-machine==2.9.0 -tomli==2.0.2 +time-machine==2.19.0 +tomli==2.3.0 + # via dependency-groups # via inline-snapshot # via mypy + # via nox # via pytest -tqdm==4.66.5 +tqdm==4.67.1 # via openai -trio==0.27.0 -types-pyaudio==0.2.16.20240516 -types-pytz==2024.2.0.20241003 +trio==0.31.0 +types-pyaudio==0.2.16.20250801 +types-pytz==2025.2.0.20251108 # via pandas-stubs -types-tqdm==4.66.0.20240417 -typing-extensions==4.12.2 +types-requests==2.32.4.20250913 + # via types-tqdm +types-tqdm==4.67.0.20250809 +typing-extensions==4.15.0 + # via aiosignal + # via anyio # via azure-core # via azure-identity + # via cryptography + # via exceptiongroup # via multidict # via mypy # via openai # via pydantic # via pydantic-core # via pyright + # via pytest-asyncio # via typing-inspection -typing-inspection==0.4.1 + # via virtualenv +typing-inspection==0.4.2 # via pydantic -tzdata==2024.1 +tzdata==2025.2 # via pandas -urllib3==2.2.1 +urllib3==2.5.0 # via requests -virtualenv==20.24.5 + # via types-requests +virtualenv==20.35.4 # via nox websockets==15.0.1 # via openai -yarl==1.20.1 +yarl==1.22.0 # via aiohttp -zipp==3.17.0 +zipp==3.23.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 386ec3c590..8e021bd69b 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,30 +12,30 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.13 +aiohttp==3.13.2 # via httpx-aiohttp # via openai -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.1.0 +anyio==4.12.0 # via httpx # via openai async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 +certifi==2025.11.12 # via httpcore # via httpx -cffi==1.17.1 +cffi==2.0.0 # via sounddevice -distro==1.8.0 +distro==1.9.0 # via openai -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio -frozenlist==1.7.0 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -47,24 +47,24 @@ httpx==0.28.1 # via openai httpx-aiohttp==0.1.9 # via openai -idna==3.4 +idna==3.11 # via anyio # via httpx # via yarl -jiter==0.11.0 +jiter==0.12.0 # via openai -multidict==6.5.0 +multidict==6.7.0 # via aiohttp # via yarl numpy==2.0.2 # via openai # via pandas # via pandas-stubs -pandas==2.2.3 +pandas==2.3.3 # via openai pandas-stubs==2.2.2.240807 # via openai -propcache==0.3.2 +propcache==0.4.1 # via aiohttp # via yarl pycparser==2.23 @@ -75,21 +75,22 @@ pydantic-core==2.41.5 # via pydantic python-dateutil==2.9.0.post0 # via pandas -pytz==2024.1 +pytz==2025.2 # via pandas -six==1.16.0 +six==1.17.0 # via python-dateutil -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via openai -sounddevice==0.5.1 +sounddevice==0.5.3 # via openai -tqdm==4.66.5 +tqdm==4.67.1 # via openai -types-pytz==2024.2.0.20241003 +types-pytz==2025.2.0.20251108 # via pandas-stubs typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via multidict # via openai # via pydantic @@ -101,5 +102,5 @@ tzdata==2025.2 # via pandas websockets==15.0.1 # via openai -yarl==1.20.1 +yarl==1.22.0 # via aiohttp From abc2596652b23318b8b5e7388b7d66fc161f817f Mon Sep 17 00:00:00 2001 From: Robert Craigie Date: Tue, 2 Dec 2025 11:29:14 +0000 Subject: [PATCH 174/408] fix(streaming): correct indentation --- src/openai/_streaming.py | 144 +++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 74e54f74fa..61a742668a 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -60,42 +60,42 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data - if sse.event and sse.event.startswith("thread."): - data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) - else: - data = sse.json() - if is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data=data, cast_to=cast_to, response=response) + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + if sse.event and sse.event.startswith("thread."): + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + else: + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) finally: # Ensure the response is closed even if the consumer doesn't read all data @@ -163,42 +163,42 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.data.startswith("[DONE]"): break - # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data - if sse.event and sse.event.startswith("thread."): - data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) - else: - data = sse.json() - if is_mapping(data) and data.get("error"): - message = None - error = data.get("error") - if is_mapping(error): - message = error.get("message") - if not message or not isinstance(message, str): - message = "An error occurred during streaming" - - raise APIError( - message=message, - request=self.response.request, - body=data["error"], - ) - - yield process_data(data=data, cast_to=cast_to, response=response) + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + if sse.event and sse.event.startswith("thread."): + data = sse.json() + + if sse.event == "error" and is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) + else: + data = sse.json() + if is_mapping(data) and data.get("error"): + message = None + error = data.get("error") + if is_mapping(error): + message = error.get("message") + if not message or not isinstance(message, str): + message = "An error occurred during streaming" + + raise APIError( + message=message, + request=self.response.request, + body=data["error"], + ) + + yield process_data(data=data, cast_to=cast_to, response=response) finally: # Ensure the response is closed even if the consumer doesn't read all data From bd988473f60e28c1ea13c9cc26d6e0b063df02b8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 21:23:11 +0000 Subject: [PATCH 175/408] chore(docs): use environment variables for authentication in code snippets --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 470707e1f3..b8050a4cd6 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ pip install openai[aiohttp] Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: ```python +import os import asyncio from openai import DefaultAioHttpClient from openai import AsyncOpenAI @@ -167,7 +168,7 @@ from openai import AsyncOpenAI async def main() -> None: async with AsyncOpenAI( - api_key="My API Key", + api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: chat_completion = await client.chat.completions.create( From 1039d5637779e035263019a687b562d3ab5d2c1a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:50:57 +0000 Subject: [PATCH 176/408] feat(api): gpt-5.1-codex-max and responses/compact --- .stats.yml | 8 +- api.md | 5 + src/openai/lib/_parsing/_responses.py | 1 + src/openai/resources/beta/assistants.py | 28 +- .../resources/beta/threads/runs/runs.py | 42 ++- .../resources/chat/completions/completions.py | 42 ++- src/openai/resources/containers/containers.py | 8 + src/openai/resources/realtime/calls.py | 46 ++- src/openai/resources/realtime/realtime.py | 4 +- src/openai/resources/responses/responses.py | 319 +++++++++++++++++- src/openai/resources/videos.py | 16 +- .../types/beta/assistant_create_params.py | 7 +- .../types/beta/assistant_update_params.py | 7 +- .../types/beta/threads/run_create_params.py | 7 +- .../types/chat/completion_create_params.py | 7 +- src/openai/types/container_create_params.py | 3 + src/openai/types/container_create_response.py | 6 + src/openai/types/container_list_response.py | 6 + .../types/container_retrieve_response.py | 6 + ...create_eval_completions_run_data_source.py | 7 +- ..._eval_completions_run_data_source_param.py | 7 +- src/openai/types/evals/run_cancel_response.py | 14 +- src/openai/types/evals/run_create_params.py | 14 +- src/openai/types/evals/run_create_response.py | 14 +- src/openai/types/evals/run_list_response.py | 14 +- .../types/evals/run_retrieve_response.py | 14 +- .../types/graders/score_model_grader.py | 7 +- .../types/graders/score_model_grader_param.py | 7 +- src/openai/types/realtime/__init__.py | 3 + .../types/realtime/call_accept_params.py | 23 +- ..._audio_buffer_dtmf_event_received_event.py | 18 + .../realtime_audio_input_turn_detection.py | 19 +- ...altime_audio_input_turn_detection_param.py | 19 +- .../types/realtime/realtime_server_event.py | 2 + .../realtime_session_create_request.py | 23 +- .../realtime_session_create_request_param.py | 23 +- .../realtime_session_create_response.py | 42 ++- ...tion_session_audio_input_turn_detection.py | 19 +- ...ession_audio_input_turn_detection_param.py | 19 +- src/openai/types/responses/__init__.py | 5 + .../types/responses/compacted_response.py | 33 ++ src/openai/types/responses/parsed_response.py | 3 +- .../responses/response_compact_params.py | 126 +++++++ .../responses/response_compaction_item.py | 20 ++ .../response_compaction_item_param.py | 18 + .../response_compaction_item_param_param.py | 18 + ...onse_function_shell_call_output_content.py | 6 +- ...unction_shell_call_output_content_param.py | 6 +- .../response_function_shell_tool_call.py | 4 +- .../types/responses/response_input_item.py | 14 +- .../responses/response_input_item_param.py | 14 +- .../types/responses/response_input_param.py | 14 +- .../types/responses/response_output_item.py | 2 + src/openai/types/responses/tool.py | 2 +- src/openai/types/responses/tool_param.py | 2 +- src/openai/types/shared/all_models.py | 1 + src/openai/types/shared/reasoning.py | 7 +- src/openai/types/shared/reasoning_effort.py | 2 +- src/openai/types/shared/responses_model.py | 1 + src/openai/types/shared_params/reasoning.py | 7 +- .../types/shared_params/reasoning_effort.py | 2 +- .../types/shared_params/responses_model.py | 1 + src/openai/types/video_create_params.py | 12 +- tests/api_resources/test_containers.py | 2 + tests/api_resources/test_responses.py | 79 ++++- 65 files changed, 1029 insertions(+), 248 deletions(-) create mode 100644 src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py create mode 100644 src/openai/types/responses/compacted_response.py create mode 100644 src/openai/types/responses/response_compact_params.py create mode 100644 src/openai/types/responses/response_compaction_item.py create mode 100644 src/openai/types/responses/response_compaction_item_param.py create mode 100644 src/openai/types/responses/response_compaction_item_param_param.py diff --git a/.stats.yml b/.stats.yml index fe1a09be6b..7adb61ca2e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 136 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a7e92d12ebe89ca019a7ac5b29759064eefa2c38fe08d03516f2620e66abb32b.yml -openapi_spec_hash: acbc703b2739447abc6312b2d753631c -config_hash: b876221dfb213df9f0a999e75d38a65e +configured_endpoints: 137 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fe8a79e6fd407e6c9afec60971f03076b65f711ccd6ea16457933b0e24fb1f6d.yml +openapi_spec_hash: 38c0a73f4e08843732c5f8002a809104 +config_hash: 2c350086d87a4b4532077363087840e7 diff --git a/api.md b/api.md index 28ee551af3..3807603206 100644 --- a/api.md +++ b/api.md @@ -733,6 +733,7 @@ Types: ```python from openai.types.responses import ( ApplyPatchTool, + CompactedResponse, ComputerTool, CustomTool, EasyInputMessage, @@ -752,6 +753,8 @@ from openai.types.responses import ( ResponseCodeInterpreterCallInProgressEvent, ResponseCodeInterpreterCallInterpretingEvent, ResponseCodeInterpreterToolCall, + ResponseCompactionItem, + ResponseCompactionItemParam, ResponseCompletedEvent, ResponseComputerToolCall, ResponseComputerToolCallOutputItem, @@ -861,6 +864,7 @@ Methods: - client.responses.retrieve(response_id, \*\*params) -> Response - client.responses.delete(response_id) -> None - client.responses.cancel(response_id) -> Response +- client.responses.compact(\*\*params) -> CompactedResponse ## InputItems @@ -914,6 +918,7 @@ from openai.types.realtime import ( InputAudioBufferClearedEvent, InputAudioBufferCommitEvent, InputAudioBufferCommittedEvent, + InputAudioBufferDtmfEventReceivedEvent, InputAudioBufferSpeechStartedEvent, InputAudioBufferSpeechStoppedEvent, InputAudioBufferTimeoutTriggered, diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 4d7b0b6224..4bed171df7 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -103,6 +103,7 @@ def parse_response( or output.type == "file_search_call" or output.type == "web_search_call" or output.type == "reasoning" + or output.type == "compaction" or output.type == "mcp_call" or output.type == "mcp_approval_request" or output.type == "image_generation_call" diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index e4ec1dca11..aa1f9f9b48 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -98,9 +98,9 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -108,6 +108,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -312,9 +313,9 @@ def update( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -322,6 +323,7 @@ def update( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -565,9 +567,9 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -575,6 +577,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -779,9 +782,9 @@ async def update( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -789,6 +792,7 @@ async def update( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index d7445d52b5..9b6cb3f752 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -169,9 +169,9 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -179,6 +179,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -330,9 +331,9 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -340,6 +341,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -487,9 +489,9 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -497,6 +499,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1620,9 +1623,9 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -1630,6 +1633,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1781,9 +1785,9 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -1791,6 +1795,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1938,9 +1943,9 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -1948,6 +1953,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index c205011d10..3f2732a608 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -411,9 +411,9 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -421,6 +421,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -721,9 +722,9 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -731,6 +732,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -1022,9 +1024,9 @@ def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -1032,6 +1034,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -1894,9 +1897,9 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -1904,6 +1907,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -2204,9 +2208,9 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -2214,6 +2218,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -2505,9 +2510,9 @@ async def create( reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -2515,6 +2520,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py index dcdc3e1a3e..0cbb400d4a 100644 --- a/src/openai/resources/containers/containers.py +++ b/src/openai/resources/containers/containers.py @@ -60,6 +60,7 @@ def create( name: str, expires_after: container_create_params.ExpiresAfter | Omit = omit, file_ids: SequenceNotStr[str] | Omit = omit, + memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -77,6 +78,8 @@ def create( file_ids: IDs of files to copy to the container. + memory_limit: Optional memory limit for the container. Defaults to "1g". + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -92,6 +95,7 @@ def create( "name": name, "expires_after": expires_after, "file_ids": file_ids, + "memory_limit": memory_limit, }, container_create_params.ContainerCreateParams, ), @@ -256,6 +260,7 @@ async def create( name: str, expires_after: container_create_params.ExpiresAfter | Omit = omit, file_ids: SequenceNotStr[str] | Omit = omit, + memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -273,6 +278,8 @@ async def create( file_ids: IDs of files to copy to the container. + memory_limit: Optional memory limit for the container. Defaults to "1g". + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -288,6 +295,7 @@ async def create( "name": name, "expires_after": expires_after, "file_ids": file_ids, + "memory_limit": memory_limit, }, container_create_params.ContainerCreateParams, ), diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index 7d2c92fe86..cdea492d95 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -199,15 +199,20 @@ def accept( limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before - truncation occurs. Clients can configure truncation behavior to truncate with a - lower max token limit, which is an effective way to control token usage and - cost. Truncation will reduce the number of cached tokens on the next turn - (busting the cache), since messages are dropped from the beginning of the - context. However, clients can also configure truncation to retain messages up to - a fraction of the maximum context size, which will reduce the need for future - truncations and thus improve the cache rate. Truncation can be disabled - entirely, which means the server will never truncate but would instead return an - error if the conversation exceeds the model's input token limit. + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. extra_headers: Send extra headers @@ -519,15 +524,20 @@ async def accept( limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before - truncation occurs. Clients can configure truncation behavior to truncate with a - lower max token limit, which is an effective way to control token usage and - cost. Truncation will reduce the number of cached tokens on the next turn - (busting the cache), since messages are dropped from the beginning of the - context. However, clients can also configure truncation to retain messages up to - a fraction of the maximum context size, which will reduce the need for future - truncations and thus improve the cache rate. Truncation can be disabled - entirely, which means the server will never truncate but would instead return an - error if the conversation exceeds the model's input token limit. + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. extra_headers: Send extra headers diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 6e69258616..33caba1871 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -829,7 +829,7 @@ def retrieve(self, *, item_id: str, event_id: str | Omit = omit) -> None: class RealtimeOutputAudioBufferResource(BaseRealtimeConnectionResource): def clear(self, *, event_id: str | Omit = omit) -> None: - """**WebRTC Only:** Emit to cut off the current audio response. + """**WebRTC/SIP Only:** Emit to cut off the current audio response. This will trigger the server to stop generating audio and emit a `output_audio_buffer.cleared` event. This @@ -1066,7 +1066,7 @@ async def retrieve(self, *, item_id: str, event_id: str | Omit = omit) -> None: class AsyncRealtimeOutputAudioBufferResource(BaseAsyncRealtimeConnectionResource): async def clear(self, *, event_id: str | Omit = omit) -> None: - """**WebRTC Only:** Emit to cut off the current audio response. + """**WebRTC/SIP Only:** Emit to cut off the current audio response. This will trigger the server to stop generating audio and emit a `output_audio_buffer.cleared` event. This diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 8365b808ae..5f081c3285 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -34,11 +34,10 @@ AsyncInputTokensWithStreamingResponse, ) from ..._base_client import make_request_options -from ...types.responses import response_create_params, response_retrieve_params -from ...lib._parsing._responses import ( - TextFormatT, - parse_response, - type_to_text_format_param as _type_to_text_format_param, +from ...types.responses import ( + response_create_params, + response_compact_params, + response_retrieve_params, ) from ...types.responses.response import Response from ...types.responses.tool_param import ToolParam, ParseableToolParam @@ -46,11 +45,13 @@ from ...types.shared_params.reasoning import Reasoning from ...types.responses.parsed_response import ParsedResponse from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager +from ...types.responses.compacted_response import CompactedResponse from ...types.responses.response_includable import ResponseIncludable from ...types.shared_params.responses_model import ResponsesModel from ...types.responses.response_input_param import ResponseInputParam from ...types.responses.response_prompt_param import ResponsePromptParam from ...types.responses.response_stream_event import ResponseStreamEvent +from ...types.responses.response_input_item_param import ResponseInputItemParam from ...types.responses.response_text_config_param import ResponseTextConfigParam __all__ = ["Responses", "AsyncResponses"] @@ -1517,6 +1518,154 @@ def cancel( cast_to=Response, ) + def compact( + self, + *, + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ] + | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompactedResponse: + """ + Compact conversation + + Args: + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/responses/compact", + body=maybe_transform( + { + "input": input, + "instructions": instructions, + "model": model, + "previous_response_id": previous_response_id, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CompactedResponse, + ) + class AsyncResponses(AsyncAPIResource): @cached_property @@ -2983,6 +3132,154 @@ async def cancel( cast_to=Response, ) + async def compact( + self, + *, + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ] + | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CompactedResponse: + """ + Compact conversation + + Args: + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/responses/compact", + body=await async_maybe_transform( + { + "input": input, + "instructions": instructions, + "model": model, + "previous_response_id": previous_response_id, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CompactedResponse, + ) + class ResponsesWithRawResponse: def __init__(self, responses: Responses) -> None: @@ -3000,9 +3297,6 @@ def __init__(self, responses: Responses) -> None: self.cancel = _legacy_response.to_raw_response_wrapper( responses.cancel, ) - self.parse = _legacy_response.to_raw_response_wrapper( - responses.parse, - ) @cached_property def input_items(self) -> InputItemsWithRawResponse: @@ -3029,9 +3323,6 @@ def __init__(self, responses: AsyncResponses) -> None: self.cancel = _legacy_response.async_to_raw_response_wrapper( responses.cancel, ) - self.parse = _legacy_response.async_to_raw_response_wrapper( - responses.parse, - ) @cached_property def input_items(self) -> AsyncInputItemsWithRawResponse: @@ -3058,6 +3349,9 @@ def __init__(self, responses: Responses) -> None: self.cancel = to_streamed_response_wrapper( responses.cancel, ) + self.compact = to_streamed_response_wrapper( + responses.compact, + ) @cached_property def input_items(self) -> InputItemsWithStreamingResponse: @@ -3084,6 +3378,9 @@ def __init__(self, responses: AsyncResponses) -> None: self.cancel = async_to_streamed_response_wrapper( responses.cancel, ) + self.compact = async_to_streamed_response_wrapper( + responses.compact, + ) @cached_property def input_items(self) -> AsyncInputItemsWithStreamingResponse: diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 4df5f02004..727091c607 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -84,11 +84,13 @@ def create( input_reference: Optional image reference that guides generation. - model: The video generation model to use. Defaults to `sora-2`. + model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults + to `sora-2`. - seconds: Clip duration in seconds. Defaults to 4 seconds. + seconds: Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds. - size: Output resolution formatted as width x height. Defaults to 720x1280. + size: Output resolution formatted as width x height (allowed values: 720x1280, + 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280. extra_headers: Send extra headers @@ -437,11 +439,13 @@ async def create( input_reference: Optional image reference that guides generation. - model: The video generation model to use. Defaults to `sora-2`. + model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults + to `sora-2`. - seconds: Clip duration in seconds. Defaults to 4 seconds. + seconds: Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds. - size: Output resolution formatted as width x height. Defaults to 720x1280. + size: Output resolution formatted as width x height (allowed values: 720x1280, + 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280. extra_headers: Send extra headers diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 009b0f49e3..38b30f212f 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -62,9 +62,9 @@ class AssistantCreateParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -72,6 +72,7 @@ class AssistantCreateParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 432116ad52..8f774c4e6c 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -97,9 +97,9 @@ class AssistantUpdateParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -107,6 +107,7 @@ class AssistantUpdateParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 74786d7d5c..df789decbc 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -111,9 +111,9 @@ class RunCreateParamsBase(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -121,6 +121,7 @@ class RunCreateParamsBase(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index e02c06cbb0..f2d55f7ec4 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -197,9 +197,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -207,6 +207,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ response_format: ResponseFormat diff --git a/src/openai/types/container_create_params.py b/src/openai/types/container_create_params.py index 01a48ac410..d629c24d38 100644 --- a/src/openai/types/container_create_params.py +++ b/src/openai/types/container_create_params.py @@ -19,6 +19,9 @@ class ContainerCreateParams(TypedDict, total=False): file_ids: SequenceNotStr[str] """IDs of files to copy to the container.""" + memory_limit: Literal["1g", "4g", "16g", "64g"] + """Optional memory limit for the container. Defaults to "1g".""" + class ExpiresAfter(TypedDict, total=False): anchor: Required[Literal["last_active_at"]] diff --git a/src/openai/types/container_create_response.py b/src/openai/types/container_create_response.py index c0ccc45a1c..cbad914283 100644 --- a/src/openai/types/container_create_response.py +++ b/src/openai/types/container_create_response.py @@ -38,3 +38,9 @@ class ContainerCreateResponse(BaseModel): point for the expiration. The minutes is the number of minutes after the anchor before the container expires. """ + + last_active_at: Optional[int] = None + """Unix timestamp (in seconds) when the container was last active.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit configured for the container.""" diff --git a/src/openai/types/container_list_response.py b/src/openai/types/container_list_response.py index 2d9c11d8a4..29416f0941 100644 --- a/src/openai/types/container_list_response.py +++ b/src/openai/types/container_list_response.py @@ -38,3 +38,9 @@ class ContainerListResponse(BaseModel): point for the expiration. The minutes is the number of minutes after the anchor before the container expires. """ + + last_active_at: Optional[int] = None + """Unix timestamp (in seconds) when the container was last active.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit configured for the container.""" diff --git a/src/openai/types/container_retrieve_response.py b/src/openai/types/container_retrieve_response.py index eab291b34f..31fedeac64 100644 --- a/src/openai/types/container_retrieve_response.py +++ b/src/openai/types/container_retrieve_response.py @@ -38,3 +38,9 @@ class ContainerRetrieveResponse(BaseModel): point for the expiration. The minutes is the number of minutes after the anchor before the container expires. """ + + last_active_at: Optional[int] = None + """Unix timestamp (in seconds) when the container was last active.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit configured for the container.""" diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 742c27a775..4236746a17 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -172,9 +172,9 @@ class SamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -182,6 +182,7 @@ class SamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ response_format: Optional[SamplingParamsResponseFormat] = None diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 18cd5018b1..751a1432b8 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -168,9 +168,9 @@ class SamplingParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -178,6 +178,7 @@ class SamplingParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ response_format: SamplingParamsResponseFormat diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index b18598b20e..f7fb0ec4ad 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -103,9 +103,9 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -113,6 +113,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -245,9 +246,9 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -255,6 +256,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index a50433f06d..a70d1923e5 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -116,9 +116,9 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -126,6 +126,7 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ temperature: Optional[float] @@ -263,9 +264,9 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -273,6 +274,7 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ seed: int diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 41dac615c7..fb2220b3a1 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -103,9 +103,9 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -113,6 +113,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -245,9 +246,9 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -255,6 +256,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index 61bff95447..adac4ffdc8 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -103,9 +103,9 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -113,6 +113,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -245,9 +246,9 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -255,6 +256,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index 651d7423a9..abdc5ebae5 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -103,9 +103,9 @@ class DataSourceResponsesSourceResponses(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -113,6 +113,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -245,9 +246,9 @@ class DataSourceResponsesSamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -255,6 +256,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index 84686a9642..b3ba6758bb 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -67,9 +67,9 @@ class SamplingParams(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -77,6 +77,7 @@ class SamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index aec7a95ad4..eb1f6e03ac 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -73,9 +73,9 @@ class SamplingParams(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -83,6 +83,7 @@ class SamplingParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ seed: Optional[int] diff --git a/src/openai/types/realtime/__init__.py b/src/openai/types/realtime/__init__.py index 83e81a034a..c2a141d727 100644 --- a/src/openai/types/realtime/__init__.py +++ b/src/openai/types/realtime/__init__.py @@ -175,6 +175,9 @@ from .response_function_call_arguments_done_event import ( ResponseFunctionCallArgumentsDoneEvent as ResponseFunctionCallArgumentsDoneEvent, ) +from .input_audio_buffer_dtmf_event_received_event import ( + InputAudioBufferDtmfEventReceivedEvent as InputAudioBufferDtmfEventReceivedEvent, +) from .realtime_conversation_item_assistant_message import ( RealtimeConversationItemAssistantMessage as RealtimeConversationItemAssistantMessage, ) diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index d6fc92b8e5..917b71cb0d 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -110,13 +110,18 @@ class CallAcceptParams(TypedDict, total=False): limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before - truncation occurs. Clients can configure truncation behavior to truncate with a - lower max token limit, which is an effective way to control token usage and - cost. Truncation will reduce the number of cached tokens on the next turn - (busting the cache), since messages are dropped from the beginning of the - context. However, clients can also configure truncation to retain messages up to - a fraction of the maximum context size, which will reduce the need for future - truncations and thus improve the cache rate. Truncation can be disabled - entirely, which means the server will never truncate but would instead return an - error if the conversation exceeds the model's input token limit. + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. """ diff --git a/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py b/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py new file mode 100644 index 0000000000..d61ed4bda7 --- /dev/null +++ b/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InputAudioBufferDtmfEventReceivedEvent"] + + +class InputAudioBufferDtmfEventReceivedEvent(BaseModel): + event: str + """The telephone keypad that was pressed by the user.""" + + received_at: int + """UTC Unix Timestamp when DTMF Event was received by server.""" + + type: Literal["input_audio_buffer.dtmf_event_received"] + """The event type, must be `input_audio_buffer.dtmf_event_received`.""" diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_audio_input_turn_detection.py index d3f4e00316..9b55353884 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection.py @@ -14,9 +14,14 @@ class ServerVad(BaseModel): """Type of turn detection, `server_vad` to turn on simple Server VAD.""" create_response: Optional[bool] = None - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ idle_timeout_ms: Optional[int] = None @@ -37,9 +42,13 @@ class ServerVad(BaseModel): interrupt_response: Optional[bool] = None """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ prefix_padding_ms: Optional[int] = None diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py index 09b8cfd159..4ce7640727 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py @@ -13,9 +13,14 @@ class ServerVad(TypedDict, total=False): """Type of turn detection, `server_vad` to turn on simple Server VAD.""" create_response: bool - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ idle_timeout_ms: Optional[int] @@ -36,9 +41,13 @@ class ServerVad(TypedDict, total=False): interrupt_response: bool """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ prefix_padding_ms: int diff --git a/src/openai/types/realtime/realtime_server_event.py b/src/openai/types/realtime/realtime_server_event.py index 1605b81a97..ead98f1a54 100644 --- a/src/openai/types/realtime/realtime_server_event.py +++ b/src/openai/types/realtime/realtime_server_event.py @@ -42,6 +42,7 @@ from .input_audio_buffer_speech_started_event import InputAudioBufferSpeechStartedEvent from .input_audio_buffer_speech_stopped_event import InputAudioBufferSpeechStoppedEvent from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .input_audio_buffer_dtmf_event_received_event import InputAudioBufferDtmfEventReceivedEvent from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent from .conversation_item_input_audio_transcription_segment import ConversationItemInputAudioTranscriptionSegment from .conversation_item_input_audio_transcription_delta_event import ConversationItemInputAudioTranscriptionDeltaEvent @@ -116,6 +117,7 @@ class OutputAudioBufferCleared(BaseModel): RealtimeErrorEvent, InputAudioBufferClearedEvent, InputAudioBufferCommittedEvent, + InputAudioBufferDtmfEventReceivedEvent, InputAudioBufferSpeechStartedEvent, InputAudioBufferSpeechStoppedEvent, RateLimitsUpdatedEvent, diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 016ae45b67..80cf468dc8 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -110,13 +110,18 @@ class RealtimeSessionCreateRequest(BaseModel): limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before - truncation occurs. Clients can configure truncation behavior to truncate with a - lower max token limit, which is an effective way to control token usage and - cost. Truncation will reduce the number of cached tokens on the next turn - (busting the cache), since messages are dropped from the beginning of the - context. However, clients can also configure truncation to retain messages up to - a fraction of the maximum context size, which will reduce the need for future - truncations and thus improve the cache rate. Truncation can be disabled - entirely, which means the server will never truncate but would instead return an - error if the conversation exceeds the model's input token limit. + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. """ diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index 8c3998c1ca..578d5a502d 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -110,13 +110,18 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before - truncation occurs. Clients can configure truncation behavior to truncate with a - lower max token limit, which is an effective way to control token usage and - cost. Truncation will reduce the number of cached tokens on the next turn - (busting the cache), since messages are dropped from the beginning of the - context. However, clients can also configure truncation to retain messages up to - a fraction of the maximum context size, which will reduce the need for future - truncations and thus improve the cache rate. Truncation can be disabled - entirely, which means the server will never truncate but would instead return an - error if the conversation exceeds the model's input token limit. + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. """ diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index c1336cd6e4..df69dd7bdb 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -53,9 +53,14 @@ class AudioInputTurnDetectionServerVad(BaseModel): """Type of turn detection, `server_vad` to turn on simple Server VAD.""" create_response: Optional[bool] = None - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ idle_timeout_ms: Optional[int] = None @@ -76,9 +81,13 @@ class AudioInputTurnDetectionServerVad(BaseModel): interrupt_response: Optional[bool] = None """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ prefix_padding_ms: Optional[int] = None @@ -463,13 +472,18 @@ class RealtimeSessionCreateResponse(BaseModel): limit, the conversation be truncated, meaning messages (starting from the oldest) will not be included in the model's context. A 32k context model with 4,096 max output tokens can only include 28,224 tokens in the context before - truncation occurs. Clients can configure truncation behavior to truncate with a - lower max token limit, which is an effective way to control token usage and - cost. Truncation will reduce the number of cached tokens on the next turn - (busting the cache), since messages are dropped from the beginning of the - context. However, clients can also configure truncation to retain messages up to - a fraction of the maximum context size, which will reduce the need for future - truncations and thus improve the cache rate. Truncation can be disabled - entirely, which means the server will never truncate but would instead return an - error if the conversation exceeds the model's input token limit. + truncation occurs. + + Clients can configure truncation behavior to truncate with a lower max token + limit, which is an effective way to control token usage and cost. + + Truncation will reduce the number of cached tokens on the next turn (busting the + cache), since messages are dropped from the beginning of the context. However, + clients can also configure truncation to retain messages up to a fraction of the + maximum context size, which will reduce the need for future truncations and thus + improve the cache rate. + + Truncation can be disabled entirely, which means the server will never truncate + but would instead return an error if the conversation exceeds the model's input + token limit. """ diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py index 7dc7a8f302..e21844f48f 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py @@ -14,9 +14,14 @@ class ServerVad(BaseModel): """Type of turn detection, `server_vad` to turn on simple Server VAD.""" create_response: Optional[bool] = None - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ idle_timeout_ms: Optional[int] = None @@ -37,9 +42,13 @@ class ServerVad(BaseModel): interrupt_response: Optional[bool] = None """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ prefix_padding_ms: Optional[int] = None diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py index d899b8c5c1..507c43141e 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py @@ -13,9 +13,14 @@ class ServerVad(TypedDict, total=False): """Type of turn detection, `server_vad` to turn on simple Server VAD.""" create_response: bool - """ - Whether or not to automatically generate a response when a VAD stop event + """Whether or not to automatically generate a response when a VAD stop event occurs. + + If `interrupt_response` is set to `false` this may fail to create a response if + the model is already responding. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ idle_timeout_ms: Optional[int] @@ -36,9 +41,13 @@ class ServerVad(TypedDict, total=False): interrupt_response: bool """ - Whether or not to automatically interrupt any ongoing response with output to - the default conversation (i.e. `conversation` of `auto`) when a VAD start event - occurs. + Whether or not to automatically interrupt (cancel) any ongoing response with + output to the default conversation (i.e. `conversation` of `auto`) when a VAD + start event occurs. If `true` then the response will be cancelled, otherwise it + will continue until complete. + + If both `create_response` and `interrupt_response` are set to `false`, the model + will never respond automatically but VAD events will still be emitted. """ prefix_padding_ms: int diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index e707141d9a..a4d939d9ff 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -28,6 +28,7 @@ from .custom_tool_param import CustomToolParam as CustomToolParam from .tool_choice_shell import ToolChoiceShell as ToolChoiceShell from .tool_choice_types import ToolChoiceTypes as ToolChoiceTypes +from .compacted_response import CompactedResponse as CompactedResponse from .easy_input_message import EasyInputMessage as EasyInputMessage from .response_item_list import ResponseItemList as ResponseItemList from .tool_choice_custom import ToolChoiceCustom as ToolChoiceCustom @@ -60,6 +61,7 @@ from .response_create_params import ResponseCreateParams as ResponseCreateParams from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent from .response_input_content import ResponseInputContent as ResponseInputContent +from .response_compact_params import ResponseCompactParams as ResponseCompactParams from .response_output_message import ResponseOutputMessage as ResponseOutputMessage from .response_output_refusal import ResponseOutputRefusal as ResponseOutputRefusal from .response_reasoning_item import ResponseReasoningItem as ResponseReasoningItem @@ -69,6 +71,7 @@ from .web_search_preview_tool import WebSearchPreviewTool as WebSearchPreviewTool from .easy_input_message_param import EasyInputMessageParam as EasyInputMessageParam from .input_token_count_params import InputTokenCountParams as InputTokenCountParams +from .response_compaction_item import ResponseCompactionItem as ResponseCompactionItem from .response_completed_event import ResponseCompletedEvent as ResponseCompletedEvent from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent @@ -108,6 +111,7 @@ from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam as ToolChoiceApplyPatchParam from .web_search_preview_tool_param import WebSearchPreviewToolParam as WebSearchPreviewToolParam from .response_apply_patch_tool_call import ResponseApplyPatchToolCall as ResponseApplyPatchToolCall +from .response_compaction_item_param import ResponseCompactionItemParam as ResponseCompactionItemParam from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent from .response_custom_tool_call_param import ResponseCustomToolCallParam as ResponseCustomToolCallParam @@ -133,6 +137,7 @@ from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent as ResponseMcpCallInProgressEvent from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent as ResponseReasoningTextDeltaEvent from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent as ResponseAudioTranscriptDoneEvent +from .response_compaction_item_param_param import ResponseCompactionItemParamParam as ResponseCompactionItemParamParam from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam as ResponseFileSearchToolCallParam from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent as ResponseMcpListToolsFailedEvent from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput as ResponseApplyPatchToolCallOutput diff --git a/src/openai/types/responses/compacted_response.py b/src/openai/types/responses/compacted_response.py new file mode 100644 index 0000000000..5b333b83c0 --- /dev/null +++ b/src/openai/types/responses/compacted_response.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .response_usage import ResponseUsage +from .response_output_item import ResponseOutputItem + +__all__ = ["CompactedResponse"] + + +class CompactedResponse(BaseModel): + id: str + """The unique identifier for the compacted response.""" + + created_at: int + """Unix timestamp (in seconds) when the compacted conversation was created.""" + + object: Literal["response.compaction"] + """The object type. Always `response.compaction`.""" + + output: List[ResponseOutputItem] + """The compacted list of output items. + + This is a list of all user messages, followed by a single compaction item. + """ + + usage: ResponseUsage + """ + Token accounting for the compaction pass, including cached, reasoning, and total + tokens. + """ diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index c120f4641d..a859710590 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -6,7 +6,6 @@ from ..._utils import PropertyInfo from .response import Response from ..._models import GenericModel -from ..._utils._transform import PropertyInfo from .response_output_item import ( McpCall, McpListTools, @@ -19,6 +18,7 @@ from .response_output_message import ResponseOutputMessage from .response_output_refusal import ResponseOutputRefusal from .response_reasoning_item import ResponseReasoningItem +from .response_compaction_item import ResponseCompactionItem from .response_custom_tool_call import ResponseCustomToolCall from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall @@ -79,6 +79,7 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): McpListTools, ResponseCodeInterpreterToolCall, ResponseCustomToolCall, + ResponseCompactionItem, ResponseFunctionShellToolCall, ResponseFunctionShellToolCallOutput, ResponseApplyPatchToolCall, diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py new file mode 100644 index 0000000000..fe38b15a9d --- /dev/null +++ b/src/openai/types/responses/response_compact_params.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, TypedDict + +from .response_input_item_param import ResponseInputItemParam + +__all__ = ["ResponseCompactParams"] + + +class ResponseCompactParams(TypedDict, total=False): + input: Union[str, Iterable[ResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + model: Union[ + Literal[ + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ] + """Model ID used to generate the response, like `gpt-5` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ diff --git a/src/openai/types/responses/response_compaction_item.py b/src/openai/types/responses/response_compaction_item.py new file mode 100644 index 0000000000..dc5f839bb8 --- /dev/null +++ b/src/openai/types/responses/response_compaction_item.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCompactionItem"] + + +class ResponseCompactionItem(BaseModel): + id: str + """The unique ID of the compaction item.""" + + encrypted_content: str + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + created_by: Optional[str] = None diff --git a/src/openai/types/responses/response_compaction_item_param.py b/src/openai/types/responses/response_compaction_item_param.py new file mode 100644 index 0000000000..8fdc2a561a --- /dev/null +++ b/src/openai/types/responses/response_compaction_item_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseCompactionItemParam"] + + +class ResponseCompactionItemParam(BaseModel): + encrypted_content: str + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + id: Optional[str] = None + """The ID of the compaction item.""" diff --git a/src/openai/types/responses/response_compaction_item_param_param.py b/src/openai/types/responses/response_compaction_item_param_param.py new file mode 100644 index 0000000000..0d12296589 --- /dev/null +++ b/src/openai/types/responses/response_compaction_item_param_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ResponseCompactionItemParamParam"] + + +class ResponseCompactionItemParamParam(TypedDict, total=False): + encrypted_content: Required[str] + + type: Required[Literal["compaction"]] + """The type of the item. Always `compaction`.""" + + id: Optional[str] + """The ID of the compaction item.""" diff --git a/src/openai/types/responses/response_function_shell_call_output_content.py b/src/openai/types/responses/response_function_shell_call_output_content.py index 1429ce9724..e0e2c09ad1 100644 --- a/src/openai/types/responses/response_function_shell_call_output_content.py +++ b/src/openai/types/responses/response_function_shell_call_output_content.py @@ -27,10 +27,10 @@ class OutcomeExit(BaseModel): class ResponseFunctionShellCallOutputContent(BaseModel): outcome: Outcome - """The exit or timeout outcome associated with this chunk.""" + """The exit or timeout outcome associated with this shell call.""" stderr: str - """Captured stderr output for this chunk of the shell call.""" + """Captured stderr output for the shell call.""" stdout: str - """Captured stdout output for this chunk of the shell call.""" + """Captured stdout output for the shell call.""" diff --git a/src/openai/types/responses/response_function_shell_call_output_content_param.py b/src/openai/types/responses/response_function_shell_call_output_content_param.py index 6395541cf5..fa065bd4b5 100644 --- a/src/openai/types/responses/response_function_shell_call_output_content_param.py +++ b/src/openai/types/responses/response_function_shell_call_output_content_param.py @@ -26,10 +26,10 @@ class OutcomeExit(TypedDict, total=False): class ResponseFunctionShellCallOutputContentParam(TypedDict, total=False): outcome: Required[Outcome] - """The exit or timeout outcome associated with this chunk.""" + """The exit or timeout outcome associated with this shell call.""" stderr: Required[str] - """Captured stderr output for this chunk of the shell call.""" + """Captured stderr output for the shell call.""" stdout: Required[str] - """Captured stdout output for this chunk of the shell call.""" + """Captured stdout output for the shell call.""" diff --git a/src/openai/types/responses/response_function_shell_tool_call.py b/src/openai/types/responses/response_function_shell_tool_call.py index be0a5bcff8..de42cb0640 100644 --- a/src/openai/types/responses/response_function_shell_tool_call.py +++ b/src/openai/types/responses/response_function_shell_tool_call.py @@ -20,7 +20,7 @@ class Action(BaseModel): class ResponseFunctionShellToolCall(BaseModel): id: str - """The unique ID of the function shell tool call. + """The unique ID of the shell tool call. Populated when this item is returned via API. """ @@ -29,7 +29,7 @@ class ResponseFunctionShellToolCall(BaseModel): """The shell commands and limits that describe how to run the tool call.""" call_id: str - """The unique ID of the function shell tool call generated by the model.""" + """The unique ID of the shell tool call generated by the model.""" status: Literal["in_progress", "completed", "incomplete"] """The status of the shell call. diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index eaf5396087..103c8634ce 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -12,6 +12,7 @@ from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch +from .response_compaction_item_param import ResponseCompactionItemParam from .response_file_search_tool_call import ResponseFileSearchToolCall from .response_custom_tool_call_output import ResponseCustomToolCallOutput from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall @@ -215,13 +216,13 @@ class ShellCall(BaseModel): """The shell commands and limits that describe how to run the tool call.""" call_id: str - """The unique ID of the function shell tool call generated by the model.""" + """The unique ID of the shell tool call generated by the model.""" type: Literal["shell_call"] - """The type of the item. Always `function_shell_call`.""" + """The type of the item. Always `shell_call`.""" id: Optional[str] = None - """The unique ID of the function shell tool call. + """The unique ID of the shell tool call. Populated when this item is returned via API. """ @@ -235,7 +236,7 @@ class ShellCall(BaseModel): class ShellCallOutput(BaseModel): call_id: str - """The unique ID of the function shell tool call generated by the model.""" + """The unique ID of the shell tool call generated by the model.""" output: List[ResponseFunctionShellCallOutputContent] """ @@ -244,10 +245,10 @@ class ShellCallOutput(BaseModel): """ type: Literal["shell_call_output"] - """The type of the item. Always `function_shell_call_output`.""" + """The type of the item. Always `shell_call_output`.""" id: Optional[str] = None - """The unique ID of the function shell tool call output. + """The unique ID of the shell tool call output. Populated when this item is returned via API. """ @@ -462,6 +463,7 @@ class ItemReference(BaseModel): ResponseFunctionToolCall, FunctionCallOutput, ResponseReasoningItem, + ResponseCompactionItemParam, ImageGenerationCall, ResponseCodeInterpreterToolCall, LocalShellCall, diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 5c2e81c4de..85d9f92b23 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -13,6 +13,7 @@ from .response_computer_tool_call_param import ResponseComputerToolCallParam from .response_function_tool_call_param import ResponseFunctionToolCallParam from .response_function_web_search_param import ResponseFunctionWebSearchParam +from .response_compaction_item_param_param import ResponseCompactionItemParamParam from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam @@ -216,13 +217,13 @@ class ShellCall(TypedDict, total=False): """The shell commands and limits that describe how to run the tool call.""" call_id: Required[str] - """The unique ID of the function shell tool call generated by the model.""" + """The unique ID of the shell tool call generated by the model.""" type: Required[Literal["shell_call"]] - """The type of the item. Always `function_shell_call`.""" + """The type of the item. Always `shell_call`.""" id: Optional[str] - """The unique ID of the function shell tool call. + """The unique ID of the shell tool call. Populated when this item is returned via API. """ @@ -236,7 +237,7 @@ class ShellCall(TypedDict, total=False): class ShellCallOutput(TypedDict, total=False): call_id: Required[str] - """The unique ID of the function shell tool call generated by the model.""" + """The unique ID of the shell tool call generated by the model.""" output: Required[Iterable[ResponseFunctionShellCallOutputContentParam]] """ @@ -245,10 +246,10 @@ class ShellCallOutput(TypedDict, total=False): """ type: Required[Literal["shell_call_output"]] - """The type of the item. Always `function_shell_call_output`.""" + """The type of the item. Always `shell_call_output`.""" id: Optional[str] - """The unique ID of the function shell tool call output. + """The unique ID of the shell tool call output. Populated when this item is returned via API. """ @@ -461,6 +462,7 @@ class ItemReference(TypedDict, total=False): ResponseFunctionToolCallParam, FunctionCallOutput, ResponseReasoningItemParam, + ResponseCompactionItemParamParam, ImageGenerationCall, ResponseCodeInterpreterToolCallParam, LocalShellCall, diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 365c6b3d7b..bbd8e6af79 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -13,6 +13,7 @@ from .response_computer_tool_call_param import ResponseComputerToolCallParam from .response_function_tool_call_param import ResponseFunctionToolCallParam from .response_function_web_search_param import ResponseFunctionWebSearchParam +from .response_compaction_item_param_param import ResponseCompactionItemParamParam from .response_file_search_tool_call_param import ResponseFileSearchToolCallParam from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam @@ -217,13 +218,13 @@ class ShellCall(TypedDict, total=False): """The shell commands and limits that describe how to run the tool call.""" call_id: Required[str] - """The unique ID of the function shell tool call generated by the model.""" + """The unique ID of the shell tool call generated by the model.""" type: Required[Literal["shell_call"]] - """The type of the item. Always `function_shell_call`.""" + """The type of the item. Always `shell_call`.""" id: Optional[str] - """The unique ID of the function shell tool call. + """The unique ID of the shell tool call. Populated when this item is returned via API. """ @@ -237,7 +238,7 @@ class ShellCall(TypedDict, total=False): class ShellCallOutput(TypedDict, total=False): call_id: Required[str] - """The unique ID of the function shell tool call generated by the model.""" + """The unique ID of the shell tool call generated by the model.""" output: Required[Iterable[ResponseFunctionShellCallOutputContentParam]] """ @@ -246,10 +247,10 @@ class ShellCallOutput(TypedDict, total=False): """ type: Required[Literal["shell_call_output"]] - """The type of the item. Always `function_shell_call_output`.""" + """The type of the item. Always `shell_call_output`.""" id: Optional[str] - """The unique ID of the function shell tool call output. + """The unique ID of the shell tool call output. Populated when this item is returned via API. """ @@ -462,6 +463,7 @@ class ItemReference(TypedDict, total=False): ResponseFunctionToolCallParam, FunctionCallOutput, ResponseReasoningItemParam, + ResponseCompactionItemParamParam, ImageGenerationCall, ResponseCodeInterpreterToolCallParam, LocalShellCall, diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index 906ddbb25e..f0a66e1836 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -7,6 +7,7 @@ from ..._models import BaseModel from .response_output_message import ResponseOutputMessage from .response_reasoning_item import ResponseReasoningItem +from .response_compaction_item import ResponseCompactionItem from .response_custom_tool_call import ResponseCustomToolCall from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall @@ -173,6 +174,7 @@ class McpApprovalRequest(BaseModel): ResponseFunctionWebSearch, ResponseComputerToolCall, ResponseReasoningItem, + ResponseCompactionItem, ImageGenerationCall, ResponseCodeInterpreterToolCall, LocalShellCall, diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index ae8b34b1f4..bb32d4e1ec 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -174,7 +174,7 @@ class CodeInterpreter(BaseModel): """The code interpreter container. Can be a container ID or an object that specifies uploaded file IDs to make - available to your code. + available to your code, along with an optional `memory_limit` setting. """ type: Literal["code_interpreter"] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 18b044ab8c..779acf0a53 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -174,7 +174,7 @@ class CodeInterpreter(TypedDict, total=False): """The code interpreter container. Can be a container ID or an object that specifies uploaded file IDs to make - available to your code. + available to your code, along with an optional `memory_limit` setting. """ type: Required[Literal["code_interpreter"]] diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py index 3e0b09e2d1..ba8e1d82cf 100644 --- a/src/openai/types/shared/all_models.py +++ b/src/openai/types/shared/all_models.py @@ -24,5 +24,6 @@ "gpt-5-codex", "gpt-5-pro", "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", ], ] diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index cf470ca057..b19476bcb5 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -14,9 +14,9 @@ class Reasoning(BaseModel): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -24,6 +24,7 @@ class Reasoning(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None diff --git a/src/openai/types/shared/reasoning_effort.py b/src/openai/types/shared/reasoning_effort.py index c890a133cc..24d8516424 100644 --- a/src/openai/types/shared/reasoning_effort.py +++ b/src/openai/types/shared/reasoning_effort.py @@ -5,4 +5,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high"]] +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py index 432cb82afd..38cdea9a94 100644 --- a/src/openai/types/shared/responses_model.py +++ b/src/openai/types/shared/responses_model.py @@ -24,5 +24,6 @@ "gpt-5-codex", "gpt-5-pro", "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", ], ] diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index ad58f70b71..71cb37c65e 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -15,9 +15,9 @@ class Reasoning(TypedDict, total=False): """ Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, and `high`. Reducing - reasoning effort can result in faster responses and fewer tokens used on - reasoning in a response. + supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. + Reducing reasoning effort can result in faster responses and fewer tokens used + on reasoning in a response. - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool @@ -25,6 +25,7 @@ class Reasoning(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. + - `xhigh` is currently only supported for `gpt-5.1-codex-max`. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] diff --git a/src/openai/types/shared_params/reasoning_effort.py b/src/openai/types/shared_params/reasoning_effort.py index e388eebff1..8518c2b141 100644 --- a/src/openai/types/shared_params/reasoning_effort.py +++ b/src/openai/types/shared_params/reasoning_effort.py @@ -7,4 +7,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high"]] +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py index fe34eb0f62..ad44dd6bf7 100644 --- a/src/openai/types/shared_params/responses_model.py +++ b/src/openai/types/shared_params/responses_model.py @@ -26,5 +26,6 @@ "gpt-5-codex", "gpt-5-pro", "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", ], ] diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py index 527d62d193..c4d3e0851f 100644 --- a/src/openai/types/video_create_params.py +++ b/src/openai/types/video_create_params.py @@ -20,10 +20,16 @@ class VideoCreateParams(TypedDict, total=False): """Optional image reference that guides generation.""" model: VideoModel - """The video generation model to use. Defaults to `sora-2`.""" + """The video generation model to use (allowed values: sora-2, sora-2-pro). + + Defaults to `sora-2`. + """ seconds: VideoSeconds - """Clip duration in seconds. Defaults to 4 seconds.""" + """Clip duration in seconds (allowed values: 4, 8, 12). Defaults to 4 seconds.""" size: VideoSize - """Output resolution formatted as width x height. Defaults to 720x1280.""" + """ + Output resolution formatted as width x height (allowed values: 720x1280, + 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280. + """ diff --git a/tests/api_resources/test_containers.py b/tests/api_resources/test_containers.py index c972f6539d..cf173c7fd5 100644 --- a/tests/api_resources/test_containers.py +++ b/tests/api_resources/test_containers.py @@ -38,6 +38,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "minutes": 0, }, file_ids=["string"], + memory_limit="1g", ) assert_matches_type(ContainerCreateResponse, container, path=["response"]) @@ -197,6 +198,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "minutes": 0, }, file_ids=["string"], + memory_limit="1g", ) assert_matches_type(ContainerCreateResponse, container, path=["response"]) diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index b57e6099c4..14e2d911ef 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -12,6 +12,7 @@ from openai._utils import assert_signatures_in_sync from openai.types.responses import ( Response, + CompactedResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -36,7 +37,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_output_tokens=0, max_tool_calls=0, metadata={"foo": "string"}, - model="gpt-4o", + model="gpt-5.1", parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -117,7 +118,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_output_tokens=0, max_tool_calls=0, metadata={"foo": "string"}, - model="gpt-4o", + model="gpt-5.1", parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -358,6 +359,41 @@ def test_path_params_cancel(self, client: OpenAI) -> None: "", ) + @parametrize + def test_method_compact(self, client: OpenAI) -> None: + response = client.responses.compact() + assert_matches_type(CompactedResponse, response, path=["response"]) + + @parametrize + def test_method_compact_with_all_params(self, client: OpenAI) -> None: + response = client.responses.compact( + input="string", + instructions="instructions", + model="gpt-5.1", + previous_response_id="resp_123", + ) + assert_matches_type(CompactedResponse, response, path=["response"]) + + @parametrize + def test_raw_response_compact(self, client: OpenAI) -> None: + http_response = client.responses.with_raw_response.compact() + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(CompactedResponse, response, path=["response"]) + + @parametrize + def test_streaming_response_compact(self, client: OpenAI) -> None: + with client.responses.with_streaming_response.compact() as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(CompactedResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_parse_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: @@ -391,7 +427,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_output_tokens=0, max_tool_calls=0, metadata={"foo": "string"}, - model="gpt-4o", + model="gpt-5.1", parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -472,7 +508,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_output_tokens=0, max_tool_calls=0, metadata={"foo": "string"}, - model="gpt-4o", + model="gpt-5.1", parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -712,3 +748,38 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: await async_client.responses.with_raw_response.cancel( "", ) + + @parametrize + async def test_method_compact(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.compact() + assert_matches_type(CompactedResponse, response, path=["response"]) + + @parametrize + async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None: + response = await async_client.responses.compact( + input="string", + instructions="instructions", + model="gpt-5.1", + previous_response_id="resp_123", + ) + assert_matches_type(CompactedResponse, response, path=["response"]) + + @parametrize + async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.responses.with_raw_response.compact() + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(CompactedResponse, response, path=["response"]) + + @parametrize + async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None: + async with async_client.responses.with_streaming_response.compact() as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(CompactedResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True From 61841f4a18d5739ee84c9c6d1aba473e05bf1bc1 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 4 Dec 2025 12:57:03 -0500 Subject: [PATCH 177/408] fix import --- src/openai/resources/responses/responses.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 5f081c3285..650a54438a 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -39,6 +39,11 @@ response_compact_params, response_retrieve_params, ) +from ...lib._parsing._responses import ( + TextFormatT, + parse_response, + type_to_text_format_param as _type_to_text_format_param, +) from ...types.responses.response import Response from ...types.responses.tool_param import ToolParam, ParseableToolParam from ...types.shared_params.metadata import Metadata From 96b9e700cef2ea78d7fb37b9a469af5af152279b Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 4 Dec 2025 13:00:31 -0500 Subject: [PATCH 178/408] manually readd --- src/openai/resources/responses/responses.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 650a54438a..2b2bdb4b37 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -3302,6 +3302,9 @@ def __init__(self, responses: Responses) -> None: self.cancel = _legacy_response.to_raw_response_wrapper( responses.cancel, ) + self.compact = _legacy_response.to_raw_response_wrapper( + responses.compact, + ) @cached_property def input_items(self) -> InputItemsWithRawResponse: @@ -3328,6 +3331,9 @@ def __init__(self, responses: AsyncResponses) -> None: self.cancel = _legacy_response.async_to_raw_response_wrapper( responses.cancel, ) + self.compact = _legacy_response.to_raw_response_wrapper( + responses.compact, + ) @cached_property def input_items(self) -> AsyncInputItemsWithRawResponse: From f45b3c3bcd7d3d40898230b8d592408bb208268a Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 4 Dec 2025 13:03:01 -0500 Subject: [PATCH 179/408] fix bad merge --- src/openai/resources/responses/responses.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 2b2bdb4b37..c532fc0bb0 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -3305,6 +3305,9 @@ def __init__(self, responses: Responses) -> None: self.compact = _legacy_response.to_raw_response_wrapper( responses.compact, ) + self.parse = _legacy_response.to_raw_response_wrapper( + responses.parse, + ) @cached_property def input_items(self) -> InputItemsWithRawResponse: @@ -3331,9 +3334,12 @@ def __init__(self, responses: AsyncResponses) -> None: self.cancel = _legacy_response.async_to_raw_response_wrapper( responses.cancel, ) - self.compact = _legacy_response.to_raw_response_wrapper( + self.compact = _legacy_response.async_to_raw_response_wrapper( responses.compact, ) + self.parse = _legacy_response.async_to_raw_response_wrapper( + responses.parse, + ) @cached_property def input_items(self) -> AsyncInputItemsWithRawResponse: From dc7602151b9042891ccd5042c7be3881337368e5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:03:39 +0000 Subject: [PATCH 180/408] release: 2.9.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 108509ed29..427b8ec423 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.8.1" + ".": "2.9.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bfa59348f..6de78290fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 2.9.0 (2025-12-04) + +Full Changelog: [v2.8.1...v2.9.0](https://github.com/openai/openai-python/compare/v2.8.1...v2.9.0) + +### Features + +* **api:** gpt-5.1-codex-max and responses/compact ([22f646e](https://github.com/openai/openai-python/commit/22f646e985b7c93782cf695edbe643844cae7017)) + + +### Bug Fixes + +* **client:** avoid mutating user-provided response config object ([#2700](https://github.com/openai/openai-python/issues/2700)) ([e040d22](https://github.com/openai/openai-python/commit/e040d22c2df068e908f69dc6b892e7f8b3fe6e99)) +* ensure streams are always closed ([0b1a27f](https://github.com/openai/openai-python/commit/0b1a27f08639d14dfe40bf80b48e2b8a1a51593c)) +* **streaming:** correct indentation ([575bbac](https://github.com/openai/openai-python/commit/575bbac13b3a57731a4e07b67636ae94463d43fa)) + + +### Chores + +* **deps:** mypy 1.18.1 has a regression, pin to 1.17 ([22cd586](https://github.com/openai/openai-python/commit/22cd586dbd5484b47f625da55db697691116b22b)) +* **docs:** use environment variables for authentication in code snippets ([c2a3cd5](https://github.com/openai/openai-python/commit/c2a3cd502bfb03f68f62f50aed15a40458c0996e)) +* **internal:** codegen related update ([307a066](https://github.com/openai/openai-python/commit/307a0664383b9d1d4151bc1a05a78c4fdcdcc9b0)) +* update lockfile ([b4109c5](https://github.com/openai/openai-python/commit/b4109c5fcf971ccfb25b4bdaef0bf36999f9eca5)) + ## 2.8.1 (2025-11-17) Full Changelog: [v2.8.0...v2.8.1](https://github.com/openai/openai-python/compare/v2.8.0...v2.8.1) diff --git a/pyproject.toml b/pyproject.toml index 3ad3b4a58a..4735412341 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.8.1" +version = "2.9.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6109cebf91..e5ddb8f4eb 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.8.1" # x-release-please-version +__version__ = "2.9.0" # x-release-please-version From ef00216846515033e4cf73ab3227e91386d958ba Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:25:07 -0500 Subject: [PATCH 181/408] release: 2.10.0 (#2767) * chore(internal): update docstring * fix(types): allow pyright to infer TypedDict types within SequenceNotStr * chore: add missing docstrings * feat(api): make model required for the responses/compact endpoint * release: 2.10.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +- CHANGELOG.md | 19 ++ pyproject.toml | 2 +- src/openai/_types.py | 5 +- src/openai/_version.py | 2 +- src/openai/resources/realtime/realtime.py | 4 +- src/openai/resources/responses/responses.py | 42 ++-- src/openai/types/audio/transcription.py | 10 + .../types/audio/transcription_diarized.py | 10 + .../audio/transcription_diarized_segment.py | 2 + .../audio/transcription_text_delta_event.py | 5 + .../audio/transcription_text_done_event.py | 9 + .../audio/transcription_text_segment_event.py | 4 + .../types/audio/transcription_verbose.py | 6 + .../auto_file_chunking_strategy_param.py | 5 + src/openai/types/batch_create_params.py | 4 + src/openai/types/batch_request_counts.py | 2 + src/openai/types/batch_usage.py | 10 + src/openai/types/beta/assistant.py | 7 + .../types/beta/assistant_create_params.py | 10 + .../types/beta/assistant_stream_event.py | 96 +++++++++ .../types/beta/assistant_tool_choice.py | 5 + .../types/beta/assistant_tool_choice_param.py | 5 + .../types/beta/assistant_update_params.py | 5 + src/openai/types/beta/chatkit/chat_session.py | 2 + .../chat_session_automatic_thread_titling.py | 2 + .../chat_session_chatkit_configuration.py | 2 + ...hat_session_chatkit_configuration_param.py | 17 ++ .../chat_session_expires_after_param.py | 2 + .../beta/chatkit/chat_session_file_upload.py | 2 + .../beta/chatkit/chat_session_history.py | 2 + .../beta/chatkit/chat_session_rate_limits.py | 2 + .../chatkit/chat_session_rate_limits_param.py | 2 + .../chatkit/chat_session_workflow_param.py | 7 + .../types/beta/chatkit/chatkit_attachment.py | 2 + .../chatkit/chatkit_response_output_text.py | 10 + .../types/beta/chatkit/chatkit_thread.py | 8 + .../chatkit_thread_assistant_message_item.py | 2 + .../beta/chatkit/chatkit_thread_item_list.py | 10 + .../chatkit_thread_user_message_item.py | 10 + .../types/beta/chatkit/chatkit_widget_item.py | 2 + .../beta/chatkit/thread_delete_response.py | 2 + src/openai/types/beta/chatkit_workflow.py | 4 + src/openai/types/beta/file_search_tool.py | 9 + .../types/beta/file_search_tool_param.py | 9 + src/openai/types/beta/thread.py | 8 + .../beta/thread_create_and_run_params.py | 25 +++ src/openai/types/beta/thread_create_params.py | 9 + src/openai/types/beta/thread_update_params.py | 4 + .../beta/threads/file_citation_annotation.py | 4 + .../threads/file_citation_delta_annotation.py | 4 + .../beta/threads/file_path_annotation.py | 4 + .../threads/file_path_delta_annotation.py | 4 + .../beta/threads/image_file_content_block.py | 4 + .../threads/image_file_content_block_param.py | 4 + .../beta/threads/image_file_delta_block.py | 4 + .../beta/threads/image_url_content_block.py | 2 + .../threads/image_url_content_block_param.py | 2 + .../beta/threads/image_url_delta_block.py | 2 + src/openai/types/beta/threads/message.py | 6 + .../types/beta/threads/message_delta.py | 2 + .../types/beta/threads/message_delta_event.py | 5 + .../beta/threads/refusal_content_block.py | 2 + .../types/beta/threads/refusal_delta_block.py | 2 + .../required_action_function_tool_call.py | 4 + src/openai/types/beta/threads/run.py | 28 +++ .../types/beta/threads/run_create_params.py | 5 + .../threads/runs/code_interpreter_logs.py | 2 + .../runs/code_interpreter_tool_call.py | 6 + .../runs/code_interpreter_tool_call_delta.py | 4 + .../threads/runs/file_search_tool_call.py | 6 + .../beta/threads/runs/function_tool_call.py | 2 + .../threads/runs/function_tool_call_delta.py | 2 + .../runs/message_creation_step_details.py | 2 + .../types/beta/threads/runs/run_step.py | 12 ++ .../types/beta/threads/runs/run_step_delta.py | 2 + .../beta/threads/runs/run_step_delta_event.py | 5 + .../runs/run_step_delta_message_delta.py | 2 + .../threads/runs/tool_call_delta_object.py | 2 + .../threads/runs/tool_calls_step_details.py | 2 + .../types/beta/threads/text_content_block.py | 2 + .../beta/threads/text_content_block_param.py | 2 + .../types/beta/threads/text_delta_block.py | 2 + src/openai/types/chat/chat_completion.py | 6 + ...at_completion_allowed_tool_choice_param.py | 2 + .../chat_completion_allowed_tools_param.py | 2 + ...chat_completion_assistant_message_param.py | 12 ++ .../types/chat/chat_completion_audio.py | 5 + .../types/chat/chat_completion_audio_param.py | 6 + .../types/chat/chat_completion_chunk.py | 15 ++ .../chat_completion_content_part_image.py | 2 + ...hat_completion_content_part_image_param.py | 2 + ...mpletion_content_part_input_audio_param.py | 2 + .../chat_completion_content_part_param.py | 4 + .../chat/chat_completion_content_part_text.py | 4 + ...chat_completion_content_part_text_param.py | 4 + .../chat/chat_completion_custom_tool_param.py | 10 + ...chat_completion_developer_message_param.py | 6 + ...t_completion_function_call_option_param.py | 4 + .../chat/chat_completion_function_tool.py | 2 + .../chat_completion_function_tool_param.py | 2 + .../types/chat/chat_completion_message.py | 11 + ...hat_completion_message_custom_tool_call.py | 4 + ...mpletion_message_custom_tool_call_param.py | 4 + ...t_completion_message_function_tool_call.py | 4 + ...letion_message_function_tool_call_param.py | 4 + ...mpletion_named_tool_choice_custom_param.py | 5 + ...chat_completion_named_tool_choice_param.py | 5 + ...hat_completion_prediction_content_param.py | 5 + .../chat/chat_completion_store_message.py | 2 + .../chat_completion_stream_options_param.py | 2 + .../chat_completion_system_message_param.py | 6 + .../chat_completion_user_message_param.py | 5 + .../types/chat/completion_create_params.py | 9 + src/openai/types/completion.py | 5 + src/openai/types/completion_usage.py | 6 + src/openai/types/container_create_params.py | 2 + src/openai/types/container_create_response.py | 6 + src/openai/types/container_list_response.py | 6 + .../types/container_retrieve_response.py | 6 + .../computer_screenshot_content.py | 2 + .../types/conversations/conversation_item.py | 18 ++ .../conversations/conversation_item_list.py | 2 + src/openai/types/conversations/message.py | 4 + .../conversations/summary_text_content.py | 2 + .../types/conversations/text_content.py | 2 + src/openai/types/create_embedding_response.py | 2 + src/openai/types/embedding.py | 2 + src/openai/types/eval_create_params.py | 37 ++++ src/openai/types/eval_create_response.py | 19 ++ .../types/eval_custom_data_source_config.py | 7 + src/openai/types/eval_list_response.py | 19 ++ src/openai/types/eval_retrieve_response.py | 19 ++ ...l_stored_completions_data_source_config.py | 2 + src/openai/types/eval_update_response.py | 19 ++ ...create_eval_completions_run_data_source.py | 16 ++ ..._eval_completions_run_data_source_param.py | 16 ++ .../create_eval_jsonl_run_data_source.py | 4 + ...create_eval_jsonl_run_data_source_param.py | 4 + src/openai/types/evals/eval_api_error.py | 2 + src/openai/types/evals/run_cancel_response.py | 28 +++ src/openai/types/evals/run_create_params.py | 24 +++ src/openai/types/evals/run_create_response.py | 28 +++ src/openai/types/evals/run_list_response.py | 28 +++ .../types/evals/run_retrieve_response.py | 28 +++ .../evals/runs/output_item_list_response.py | 10 + .../runs/output_item_retrieve_response.py | 10 + src/openai/types/file_create_params.py | 5 + src/openai/types/file_object.py | 2 + .../checkpoints/permission_create_response.py | 4 + .../permission_retrieve_response.py | 4 + .../types/fine_tuning/dpo_hyperparameters.py | 2 + .../fine_tuning/dpo_hyperparameters_param.py | 2 + src/openai/types/fine_tuning/dpo_method.py | 2 + .../types/fine_tuning/dpo_method_param.py | 2 + .../types/fine_tuning/fine_tuning_job.py | 15 ++ .../fine_tuning/fine_tuning_job_event.py | 2 + .../fine_tuning_job_wandb_integration.py | 7 + .../types/fine_tuning/job_create_params.py | 14 ++ .../jobs/fine_tuning_job_checkpoint.py | 6 + .../reinforcement_hyperparameters.py | 2 + .../reinforcement_hyperparameters_param.py | 2 + .../types/fine_tuning/reinforcement_method.py | 2 + .../fine_tuning/reinforcement_method_param.py | 2 + .../fine_tuning/supervised_hyperparameters.py | 2 + .../supervised_hyperparameters_param.py | 2 + .../types/fine_tuning/supervised_method.py | 2 + .../fine_tuning/supervised_method_param.py | 2 + .../types/graders/label_model_grader.py | 17 ++ .../types/graders/label_model_grader_param.py | 17 ++ src/openai/types/graders/multi_grader.py | 4 + .../types/graders/multi_grader_param.py | 4 + src/openai/types/graders/python_grader.py | 2 + .../types/graders/python_grader_param.py | 2 + .../types/graders/score_model_grader.py | 16 ++ .../types/graders/score_model_grader_param.py | 16 ++ .../types/graders/string_check_grader.py | 4 + .../graders/string_check_grader_param.py | 4 + .../types/graders/text_similarity_grader.py | 2 + .../graders/text_similarity_grader_param.py | 2 + src/openai/types/image.py | 2 + .../types/image_edit_completed_event.py | 6 + .../types/image_edit_partial_image_event.py | 2 + src/openai/types/image_gen_completed_event.py | 6 + .../types/image_gen_partial_image_event.py | 2 + src/openai/types/images_response.py | 6 + src/openai/types/model.py | 2 + src/openai/types/moderation.py | 8 + .../types/moderation_create_response.py | 2 + .../types/moderation_image_url_input_param.py | 4 + .../types/moderation_text_input_param.py | 2 + .../other_file_chunking_strategy_object.py | 5 + .../realtime/client_secret_create_params.py | 8 + .../realtime/client_secret_create_response.py | 2 + .../realtime/conversation_created_event.py | 4 + .../types/realtime/conversation_item_added.py | 10 + .../conversation_item_create_event.py | 10 + .../conversation_item_create_event_param.py | 10 + .../conversation_item_created_event.py | 13 ++ .../conversation_item_delete_event.py | 8 + .../conversation_item_delete_event_param.py | 8 + .../conversation_item_deleted_event.py | 6 + .../types/realtime/conversation_item_done.py | 5 + ...put_audio_transcription_completed_event.py | 19 ++ ...m_input_audio_transcription_delta_event.py | 4 + ..._input_audio_transcription_failed_event.py | 8 + ..._item_input_audio_transcription_segment.py | 2 + .../conversation_item_retrieve_event.py | 7 + .../conversation_item_retrieve_event_param.py | 7 + .../conversation_item_truncate_event.py | 15 ++ .../conversation_item_truncate_event_param.py | 15 ++ .../conversation_item_truncated_event.py | 9 + .../input_audio_buffer_append_event.py | 17 ++ .../input_audio_buffer_append_event_param.py | 17 ++ .../input_audio_buffer_clear_event.py | 6 + .../input_audio_buffer_clear_event_param.py | 6 + .../input_audio_buffer_cleared_event.py | 5 + .../input_audio_buffer_commit_event.py | 6 + .../input_audio_buffer_commit_event_param.py | 6 + .../input_audio_buffer_committed_event.py | 7 + ..._audio_buffer_dtmf_event_received_event.py | 8 + ...input_audio_buffer_speech_started_event.py | 13 ++ ...input_audio_buffer_speech_stopped_event.py | 6 + .../input_audio_buffer_timeout_triggered.py | 17 ++ .../types/realtime/log_prob_properties.py | 2 + .../realtime/mcp_list_tools_completed.py | 2 + .../types/realtime/mcp_list_tools_failed.py | 2 + .../realtime/mcp_list_tools_in_progress.py | 2 + .../output_audio_buffer_clear_event.py | 9 + .../output_audio_buffer_clear_event_param.py | 9 + .../realtime/rate_limits_updated_event.py | 8 + .../types/realtime/realtime_audio_config.py | 2 + .../realtime/realtime_audio_config_input.py | 7 + .../realtime_audio_config_input_param.py | 7 + .../realtime/realtime_audio_config_param.py | 2 + .../types/realtime/realtime_audio_formats.py | 6 + .../realtime/realtime_audio_formats_param.py | 6 + .../realtime_audio_input_turn_detection.py | 8 + ...altime_audio_input_turn_detection_param.py | 8 + ...ime_conversation_item_assistant_message.py | 2 + ...nversation_item_assistant_message_param.py | 2 + ...ealtime_conversation_item_function_call.py | 2 + ..._conversation_item_function_call_output.py | 2 + ...rsation_item_function_call_output_param.py | 2 + ...e_conversation_item_function_call_param.py | 2 + ...altime_conversation_item_system_message.py | 4 + ..._conversation_item_system_message_param.py | 4 + ...realtime_conversation_item_user_message.py | 2 + ...me_conversation_item_user_message_param.py | 2 + src/openai/types/realtime/realtime_error.py | 2 + .../types/realtime/realtime_error_event.py | 6 + .../realtime/realtime_mcp_approval_request.py | 2 + .../realtime_mcp_approval_request_param.py | 2 + .../realtime_mcp_approval_response.py | 2 + .../realtime_mcp_approval_response_param.py | 2 + .../types/realtime/realtime_mcp_list_tools.py | 4 + .../realtime/realtime_mcp_list_tools_param.py | 4 + .../types/realtime/realtime_mcp_tool_call.py | 2 + .../realtime/realtime_mcp_tool_call_param.py | 2 + .../types/realtime/realtime_response.py | 4 + .../realtime_response_create_audio_output.py | 2 + ...time_response_create_audio_output_param.py | 2 + .../realtime_response_create_mcp_tool.py | 18 ++ ...realtime_response_create_mcp_tool_param.py | 18 ++ .../realtime_response_create_params.py | 2 + .../realtime_response_create_params_param.py | 2 + .../realtime/realtime_response_status.py | 7 + .../types/realtime/realtime_response_usage.py | 8 + ...time_response_usage_input_token_details.py | 7 + ...ime_response_usage_output_token_details.py | 2 + .../types/realtime/realtime_server_event.py | 28 +++ .../realtime_session_client_secret.py | 2 + .../realtime_session_create_request.py | 2 + .../realtime_session_create_request_param.py | 2 + .../realtime_session_create_response.py | 43 ++++ .../realtime/realtime_tools_config_param.py | 18 ++ .../realtime/realtime_tools_config_union.py | 18 ++ .../realtime_tools_config_union_param.py | 18 ++ .../types/realtime/realtime_tracing_config.py | 2 + .../realtime/realtime_tracing_config_param.py | 2 + .../realtime_transcription_session_audio.py | 2 + ...ltime_transcription_session_audio_input.py | 7 + ...transcription_session_audio_input_param.py | 7 + ...tion_session_audio_input_turn_detection.py | 8 + ...ession_audio_input_turn_detection_param.py | 8 + ...ltime_transcription_session_audio_param.py | 2 + ...me_transcription_session_create_request.py | 2 + ...nscription_session_create_request_param.py | 2 + ...e_transcription_session_create_response.py | 6 + ...me_transcription_session_turn_detection.py | 7 + .../realtime_truncation_retention_ratio.py | 9 + ...altime_truncation_retention_ratio_param.py | 9 + .../realtime/response_audio_delta_event.py | 2 + .../realtime/response_audio_done_event.py | 6 + .../response_audio_transcript_delta_event.py | 2 + .../response_audio_transcript_done_event.py | 6 + .../types/realtime/response_cancel_event.py | 9 + .../realtime/response_cancel_event_param.py | 9 + .../response_content_part_added_event.py | 7 + .../response_content_part_done_event.py | 7 + .../types/realtime/response_create_event.py | 28 +++ .../realtime/response_create_event_param.py | 28 +++ .../types/realtime/response_created_event.py | 6 + .../types/realtime/response_done_event.py | 13 ++ ...nse_function_call_arguments_delta_event.py | 2 + ...onse_function_call_arguments_done_event.py | 5 + .../response_mcp_call_arguments_delta.py | 2 + .../response_mcp_call_arguments_done.py | 2 + .../realtime/response_mcp_call_completed.py | 2 + .../realtime/response_mcp_call_failed.py | 2 + .../realtime/response_mcp_call_in_progress.py | 2 + .../response_output_item_added_event.py | 2 + .../response_output_item_done_event.py | 6 + .../realtime/response_text_delta_event.py | 2 + .../realtime/response_text_done_event.py | 6 + .../types/realtime/session_created_event.py | 7 + .../types/realtime/session_update_event.py | 12 ++ .../realtime/session_update_event_param.py | 12 ++ .../types/realtime/session_updated_event.py | 5 + .../types/responses/apply_patch_tool.py | 2 + .../types/responses/apply_patch_tool_param.py | 2 + src/openai/types/responses/computer_tool.py | 5 + .../types/responses/computer_tool_param.py | 5 + src/openai/types/responses/custom_tool.py | 5 + .../types/responses/custom_tool_param.py | 5 + .../types/responses/easy_input_message.py | 8 + .../responses/easy_input_message_param.py | 8 + .../types/responses/file_search_tool.py | 11 + .../types/responses/file_search_tool_param.py | 11 + .../types/responses/function_shell_tool.py | 2 + .../responses/function_shell_tool_param.py | 2 + src/openai/types/responses/function_tool.py | 5 + .../types/responses/function_tool_param.py | 5 + .../responses/input_token_count_params.py | 8 + src/openai/types/responses/response.py | 7 + .../response_apply_patch_tool_call.py | 8 + .../response_apply_patch_tool_call_output.py | 2 + .../responses/response_audio_delta_event.py | 2 + .../responses/response_audio_done_event.py | 2 + .../response_audio_transcript_delta_event.py | 2 + .../response_audio_transcript_done_event.py | 2 + ..._code_interpreter_call_code_delta_event.py | 2 + ...e_code_interpreter_call_code_done_event.py | 2 + ...e_code_interpreter_call_completed_event.py | 2 + ...code_interpreter_call_in_progress_event.py | 2 + ...ode_interpreter_call_interpreting_event.py | 2 + .../response_code_interpreter_tool_call.py | 6 + ...sponse_code_interpreter_tool_call_param.py | 6 + .../responses/response_compact_params.py | 194 +++++++++--------- .../responses/response_compaction_item.py | 4 + .../response_compaction_item_param.py | 4 + .../response_compaction_item_param_param.py | 4 + .../responses/response_completed_event.py | 2 + .../responses/response_computer_tool_call.py | 28 +++ ...response_computer_tool_call_output_item.py | 2 + ...se_computer_tool_call_output_screenshot.py | 2 + ...puter_tool_call_output_screenshot_param.py | 2 + .../response_computer_tool_call_param.py | 28 +++ .../response_content_part_added_event.py | 4 + .../response_content_part_done_event.py | 4 + .../responses/response_conversation_param.py | 2 + .../types/responses/response_create_params.py | 2 + .../types/responses/response_created_event.py | 2 + .../responses/response_custom_tool_call.py | 2 + ...onse_custom_tool_call_input_delta_event.py | 2 + ...ponse_custom_tool_call_input_done_event.py | 2 + .../response_custom_tool_call_output.py | 2 + .../response_custom_tool_call_output_param.py | 2 + .../response_custom_tool_call_param.py | 2 + src/openai/types/responses/response_error.py | 2 + .../types/responses/response_error_event.py | 2 + .../types/responses/response_failed_event.py | 2 + ...sponse_file_search_call_completed_event.py | 2 + ...onse_file_search_call_in_progress_event.py | 2 + ...sponse_file_search_call_searching_event.py | 2 + .../response_file_search_tool_call.py | 6 + .../response_file_search_tool_call_param.py | 6 + ...response_format_text_json_schema_config.py | 6 + ...se_format_text_json_schema_config_param.py | 6 + ...nse_function_call_arguments_delta_event.py | 2 + ...onse_function_call_arguments_done_event.py | 2 + ...onse_function_shell_call_output_content.py | 6 + ...unction_shell_call_output_content_param.py | 6 + .../response_function_shell_tool_call.py | 4 + ...esponse_function_shell_tool_call_output.py | 8 + .../responses/response_function_tool_call.py | 6 + .../response_function_tool_call_item.py | 6 + .../response_function_tool_call_param.py | 6 + .../responses/response_function_web_search.py | 14 ++ .../response_function_web_search_param.py | 14 ++ ...response_image_gen_call_completed_event.py | 4 + ...esponse_image_gen_call_generating_event.py | 4 + ...sponse_image_gen_call_in_progress_event.py | 2 + ...onse_image_gen_call_partial_image_event.py | 2 + .../responses/response_in_progress_event.py | 2 + .../responses/response_incomplete_event.py | 2 + .../types/responses/response_input_audio.py | 2 + .../responses/response_input_audio_param.py | 2 + .../types/responses/response_input_file.py | 2 + .../responses/response_input_file_content.py | 2 + .../response_input_file_content_param.py | 2 + .../responses/response_input_file_param.py | 2 + .../types/responses/response_input_image.py | 5 + .../responses/response_input_image_content.py | 5 + .../response_input_image_content_param.py | 5 + .../responses/response_input_image_param.py | 5 + .../types/responses/response_input_item.py | 50 +++++ .../responses/response_input_item_param.py | 50 +++++ .../types/responses/response_input_param.py | 50 +++++ .../types/responses/response_input_text.py | 2 + .../responses/response_input_text_content.py | 2 + .../response_input_text_content_param.py | 2 + .../responses/response_input_text_param.py | 2 + src/openai/types/responses/response_item.py | 18 ++ .../types/responses/response_item_list.py | 2 + ...response_mcp_call_arguments_delta_event.py | 4 + .../response_mcp_call_arguments_done_event.py | 2 + .../response_mcp_call_completed_event.py | 2 + .../response_mcp_call_failed_event.py | 2 + .../response_mcp_call_in_progress_event.py | 2 + ...response_mcp_list_tools_completed_event.py | 2 + .../response_mcp_list_tools_failed_event.py | 2 + ...sponse_mcp_list_tools_in_progress_event.py | 4 + .../types/responses/response_output_item.py | 14 ++ .../response_output_item_added_event.py | 2 + .../response_output_item_done_event.py | 2 + .../responses/response_output_message.py | 2 + .../response_output_message_param.py | 2 + .../responses/response_output_refusal.py | 2 + .../response_output_refusal_param.py | 2 + .../types/responses/response_output_text.py | 14 ++ ...onse_output_text_annotation_added_event.py | 2 + .../responses/response_output_text_param.py | 14 ++ src/openai/types/responses/response_prompt.py | 5 + .../types/responses/response_prompt_param.py | 5 + .../types/responses/response_queued_event.py | 2 + .../responses/response_reasoning_item.py | 11 + .../response_reasoning_item_param.py | 11 + ...onse_reasoning_summary_part_added_event.py | 4 + ...ponse_reasoning_summary_part_done_event.py | 4 + ...onse_reasoning_summary_text_delta_event.py | 2 + ...ponse_reasoning_summary_text_done_event.py | 2 + .../response_reasoning_text_delta_event.py | 2 + .../response_reasoning_text_done_event.py | 2 + .../responses/response_refusal_delta_event.py | 2 + .../responses/response_refusal_done_event.py | 2 + .../types/responses/response_text_config.py | 8 + .../responses/response_text_config_param.py | 8 + .../responses/response_text_delta_event.py | 8 + .../responses/response_text_done_event.py | 8 + src/openai/types/responses/response_usage.py | 9 + ...esponse_web_search_call_completed_event.py | 2 + ...ponse_web_search_call_in_progress_event.py | 2 + ...esponse_web_search_call_searching_event.py | 2 + src/openai/types/responses/tool.py | 35 ++++ .../types/responses/tool_choice_allowed.py | 2 + .../responses/tool_choice_allowed_param.py | 2 + .../responses/tool_choice_apply_patch.py | 2 + .../tool_choice_apply_patch_param.py | 2 + .../types/responses/tool_choice_custom.py | 2 + .../responses/tool_choice_custom_param.py | 2 + .../types/responses/tool_choice_function.py | 2 + .../responses/tool_choice_function_param.py | 2 + src/openai/types/responses/tool_choice_mcp.py | 4 + .../types/responses/tool_choice_mcp_param.py | 4 + .../types/responses/tool_choice_shell.py | 2 + .../responses/tool_choice_shell_param.py | 2 + .../types/responses/tool_choice_types.py | 5 + .../responses/tool_choice_types_param.py | 5 + src/openai/types/responses/tool_param.py | 35 ++++ .../responses/web_search_preview_tool.py | 7 + .../web_search_preview_tool_param.py | 7 + src/openai/types/responses/web_search_tool.py | 10 + .../types/responses/web_search_tool_param.py | 10 + src/openai/types/shared/comparison_filter.py | 4 + src/openai/types/shared/compound_filter.py | 2 + .../types/shared/custom_tool_input_format.py | 4 + src/openai/types/shared/reasoning.py | 6 + .../shared/response_format_json_object.py | 8 + .../shared/response_format_json_schema.py | 8 + .../types/shared/response_format_text.py | 2 + .../shared/response_format_text_grammar.py | 5 + .../shared/response_format_text_python.py | 6 + .../types/shared_params/comparison_filter.py | 4 + .../types/shared_params/compound_filter.py | 2 + .../shared_params/custom_tool_input_format.py | 4 + src/openai/types/shared_params/reasoning.py | 6 + .../response_format_json_object.py | 8 + .../response_format_json_schema.py | 8 + .../shared_params/response_format_text.py | 2 + ...tic_file_chunking_strategy_object_param.py | 2 + src/openai/types/upload.py | 2 + src/openai/types/upload_create_params.py | 5 + src/openai/types/uploads/upload_part.py | 2 + src/openai/types/vector_store.py | 6 + .../types/vector_store_create_params.py | 2 + .../types/vector_store_search_params.py | 2 + .../types/vector_store_update_params.py | 2 + .../types/vector_stores/vector_store_file.py | 7 + .../vector_stores/vector_store_file_batch.py | 2 + src/openai/types/video.py | 2 + src/openai/types/video_delete_response.py | 2 + .../webhooks/batch_cancelled_webhook_event.py | 4 + .../webhooks/batch_completed_webhook_event.py | 4 + .../webhooks/batch_expired_webhook_event.py | 4 + .../webhooks/batch_failed_webhook_event.py | 4 + .../eval_run_canceled_webhook_event.py | 4 + .../webhooks/eval_run_failed_webhook_event.py | 4 + .../eval_run_succeeded_webhook_event.py | 4 + ...fine_tuning_job_cancelled_webhook_event.py | 4 + .../fine_tuning_job_failed_webhook_event.py | 4 + ...fine_tuning_job_succeeded_webhook_event.py | 4 + .../realtime_call_incoming_webhook_event.py | 6 + .../response_cancelled_webhook_event.py | 4 + .../response_completed_webhook_event.py | 4 + .../webhooks/response_failed_webhook_event.py | 4 + .../response_incomplete_webhook_event.py | 4 + tests/api_resources/test_responses.py | 28 ++- 519 files changed, 3370 insertions(+), 136 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 427b8ec423..21f60560ae 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.9.0" + ".": "2.10.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 7adb61ca2e..0aa25fb4a4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fe8a79e6fd407e6c9afec60971f03076b65f711ccd6ea16457933b0e24fb1f6d.yml -openapi_spec_hash: 38c0a73f4e08843732c5f8002a809104 -config_hash: 2c350086d87a4b4532077363087840e7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-88d85ff87ad8983262af2b729762a6e05fd509468bb691529bc2f81e4ce27c69.yml +openapi_spec_hash: 46a55acbccd0147534017b92c1f4dd99 +config_hash: 141b101c9f13b90e21af74e1686f1f41 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6de78290fc..58a092665e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 2.10.0 (2025-12-10) + +Full Changelog: [v2.9.0...v2.10.0](https://github.com/openai/openai-python/compare/v2.9.0...v2.10.0) + +### Features + +* **api:** make model required for the responses/compact endpoint ([a12936b](https://github.com/openai/openai-python/commit/a12936b18cf19009d4e6d586c9b1958359636dbe)) + + +### Bug Fixes + +* **types:** allow pyright to infer TypedDict types within SequenceNotStr ([8f0d230](https://github.com/openai/openai-python/commit/8f0d23066c1edc38a6e9858b054dceaf92ae001b)) + + +### Chores + +* add missing docstrings ([f20a9a1](https://github.com/openai/openai-python/commit/f20a9a18a421ba69622c77ab539509d218e774eb)) +* **internal:** update docstring ([9a993f2](https://github.com/openai/openai-python/commit/9a993f2261b6524aa30b955e006c7ea89f086968)) + ## 2.9.0 (2025-12-04) Full Changelog: [v2.8.1...v2.9.0](https://github.com/openai/openai-python/compare/v2.8.1...v2.9.0) diff --git a/pyproject.toml b/pyproject.toml index 4735412341..e7d181d007 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.9.0" +version = "2.10.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_types.py b/src/openai/_types.py index 2387d7e01c..d7e2eaac5f 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -247,6 +247,9 @@ class HttpxSendArgs(TypedDict, total=False): if TYPE_CHECKING: # This works because str.__contains__ does not accept object (either in typeshed or at runtime) # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. class SequenceNotStr(Protocol[_T_co]): @overload def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... @@ -255,8 +258,6 @@ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... def __contains__(self, value: object, /) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_T_co]: ... - def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... - def count(self, value: Any, /) -> int: ... def __reversed__(self) -> Iterator[_T_co]: ... else: # just point this to a normal `Sequence` at runtime to avoid having to special case diff --git a/src/openai/_version.py b/src/openai/_version.py index e5ddb8f4eb..c7a4f08f04 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.9.0" # x-release-please-version +__version__ = "2.10.0" # x-release-please-version diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 33caba1871..44f14cd3aa 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -232,7 +232,7 @@ def calls(self) -> AsyncCallsWithStreamingResponse: class AsyncRealtimeConnection: - """Represents a live websocket connection to the Realtime API""" + """Represents a live WebSocket connection to the Realtime API""" session: AsyncRealtimeSessionResource response: AsyncRealtimeResponseResource @@ -421,7 +421,7 @@ async def __aexit__( class RealtimeConnection: - """Represents a live websocket connection to the Realtime API""" + """Represents a live WebSocket connection to the Realtime API""" session: RealtimeSessionResource response: RealtimeResponseResource diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index c532fc0bb0..81e8980faf 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1526,8 +1526,6 @@ def cancel( def compact( self, *, - input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, - instructions: Optional[str] | Omit = omit, model: Union[ Literal[ "gpt-5.1", @@ -1614,8 +1612,9 @@ def compact( ], str, None, - ] - | Omit = omit, + ], + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1628,6 +1627,12 @@ def compact( Compact conversation Args: + model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + input: Text, image, or file inputs to the model, used to generate a response instructions: A system (or developer) message inserted into the model's context. When used @@ -1635,12 +1640,6 @@ def compact( will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses. - model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). @@ -1658,9 +1657,9 @@ def compact( "/responses/compact", body=maybe_transform( { + "model": model, "input": input, "instructions": instructions, - "model": model, "previous_response_id": previous_response_id, }, response_compact_params.ResponseCompactParams, @@ -3140,8 +3139,6 @@ async def cancel( async def compact( self, *, - input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, - instructions: Optional[str] | Omit = omit, model: Union[ Literal[ "gpt-5.1", @@ -3228,8 +3225,9 @@ async def compact( ], str, None, - ] - | Omit = omit, + ], + input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -3242,6 +3240,12 @@ async def compact( Compact conversation Args: + model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + input: Text, image, or file inputs to the model, used to generate a response instructions: A system (or developer) message inserted into the model's context. When used @@ -3249,12 +3253,6 @@ async def compact( will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses. - model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a - wide range of models with different capabilities, performance characteristics, - and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). @@ -3272,9 +3270,9 @@ async def compact( "/responses/compact", body=await async_maybe_transform( { + "model": model, "input": input, "instructions": instructions, - "model": model, "previous_response_id": previous_response_id, }, response_compact_params.ResponseCompactParams, diff --git a/src/openai/types/audio/transcription.py b/src/openai/types/audio/transcription.py index 4c5882152d..cbae8bf750 100644 --- a/src/openai/types/audio/transcription.py +++ b/src/openai/types/audio/transcription.py @@ -21,6 +21,8 @@ class Logprob(BaseModel): class UsageTokensInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + audio_tokens: Optional[int] = None """Number of audio tokens billed for this request.""" @@ -29,6 +31,8 @@ class UsageTokensInputTokenDetails(BaseModel): class UsageTokens(BaseModel): + """Usage statistics for models billed by token usage.""" + input_tokens: int """Number of input tokens billed for this request.""" @@ -46,6 +50,8 @@ class UsageTokens(BaseModel): class UsageDuration(BaseModel): + """Usage statistics for models billed by audio input duration.""" + seconds: float """Duration of the input audio in seconds.""" @@ -57,6 +63,10 @@ class UsageDuration(BaseModel): class Transcription(BaseModel): + """ + Represents a transcription response returned by model, based on the provided input. + """ + text: str """The transcribed text.""" diff --git a/src/openai/types/audio/transcription_diarized.py b/src/openai/types/audio/transcription_diarized.py index b7dd2b8ebb..07585fe239 100644 --- a/src/openai/types/audio/transcription_diarized.py +++ b/src/openai/types/audio/transcription_diarized.py @@ -11,6 +11,8 @@ class UsageTokensInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + audio_tokens: Optional[int] = None """Number of audio tokens billed for this request.""" @@ -19,6 +21,8 @@ class UsageTokensInputTokenDetails(BaseModel): class UsageTokens(BaseModel): + """Usage statistics for models billed by token usage.""" + input_tokens: int """Number of input tokens billed for this request.""" @@ -36,6 +40,8 @@ class UsageTokens(BaseModel): class UsageDuration(BaseModel): + """Usage statistics for models billed by audio input duration.""" + seconds: float """Duration of the input audio in seconds.""" @@ -47,6 +53,10 @@ class UsageDuration(BaseModel): class TranscriptionDiarized(BaseModel): + """ + Represents a diarized transcription response returned by the model, including the combined transcript and speaker-segment annotations. + """ + duration: float """Duration of the input audio in seconds.""" diff --git a/src/openai/types/audio/transcription_diarized_segment.py b/src/openai/types/audio/transcription_diarized_segment.py index fe87bb4fb8..fcfdb3634f 100644 --- a/src/openai/types/audio/transcription_diarized_segment.py +++ b/src/openai/types/audio/transcription_diarized_segment.py @@ -8,6 +8,8 @@ class TranscriptionDiarizedSegment(BaseModel): + """A segment of diarized transcript text with speaker metadata.""" + id: str """Unique identifier for the segment.""" diff --git a/src/openai/types/audio/transcription_text_delta_event.py b/src/openai/types/audio/transcription_text_delta_event.py index 363b6a6335..a6e83133c8 100644 --- a/src/openai/types/audio/transcription_text_delta_event.py +++ b/src/openai/types/audio/transcription_text_delta_event.py @@ -20,6 +20,11 @@ class Logprob(BaseModel): class TranscriptionTextDeltaEvent(BaseModel): + """Emitted when there is an additional text delta. + + This is also the first event emitted when the transcription starts. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `Stream` parameter set to `true`. + """ + delta: str """The text delta that was additionally transcribed.""" diff --git a/src/openai/types/audio/transcription_text_done_event.py b/src/openai/types/audio/transcription_text_done_event.py index 9665edc565..c8f7fc0769 100644 --- a/src/openai/types/audio/transcription_text_done_event.py +++ b/src/openai/types/audio/transcription_text_done_event.py @@ -20,6 +20,8 @@ class Logprob(BaseModel): class UsageInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + audio_tokens: Optional[int] = None """Number of audio tokens billed for this request.""" @@ -28,6 +30,8 @@ class UsageInputTokenDetails(BaseModel): class Usage(BaseModel): + """Usage statistics for models billed by token usage.""" + input_tokens: int """Number of input tokens billed for this request.""" @@ -45,6 +49,11 @@ class Usage(BaseModel): class TranscriptionTextDoneEvent(BaseModel): + """Emitted when the transcription is complete. + + Contains the complete transcription text. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with the `Stream` parameter set to `true`. + """ + text: str """The text that was transcribed.""" diff --git a/src/openai/types/audio/transcription_text_segment_event.py b/src/openai/types/audio/transcription_text_segment_event.py index d4f7664578..e95472e6c6 100644 --- a/src/openai/types/audio/transcription_text_segment_event.py +++ b/src/openai/types/audio/transcription_text_segment_event.py @@ -8,6 +8,10 @@ class TranscriptionTextSegmentEvent(BaseModel): + """ + Emitted when a diarized transcription returns a completed segment with speaker information. Only emitted when you [create a transcription](https://platform.openai.com/docs/api-reference/audio/create-transcription) with `stream` set to `true` and `response_format` set to `diarized_json`. + """ + id: str """Unique identifier for the segment.""" diff --git a/src/openai/types/audio/transcription_verbose.py b/src/openai/types/audio/transcription_verbose.py index addda71ec6..b1a95e9c72 100644 --- a/src/openai/types/audio/transcription_verbose.py +++ b/src/openai/types/audio/transcription_verbose.py @@ -11,6 +11,8 @@ class Usage(BaseModel): + """Usage statistics for models billed by audio input duration.""" + seconds: float """Duration of the input audio in seconds.""" @@ -19,6 +21,10 @@ class Usage(BaseModel): class TranscriptionVerbose(BaseModel): + """ + Represents a verbose json transcription response returned by model, based on the provided input. + """ + duration: float """The duration of the input audio.""" diff --git a/src/openai/types/auto_file_chunking_strategy_param.py b/src/openai/types/auto_file_chunking_strategy_param.py index 6f17836bac..db7cbf596d 100644 --- a/src/openai/types/auto_file_chunking_strategy_param.py +++ b/src/openai/types/auto_file_chunking_strategy_param.py @@ -8,5 +8,10 @@ class AutoFileChunkingStrategyParam(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + type: Required[Literal["auto"]] """Always `auto`.""" diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index c182a87e7f..1088aab380 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -58,6 +58,10 @@ class BatchCreateParams(TypedDict, total=False): class OutputExpiresAfter(TypedDict, total=False): + """ + The expiration policy for the output and/or error file that are generated for a batch. + """ + anchor: Required[Literal["created_at"]] """Anchor timestamp after which the expiration policy applies. diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py index 068b071af1..64a570747d 100644 --- a/src/openai/types/batch_request_counts.py +++ b/src/openai/types/batch_request_counts.py @@ -6,6 +6,8 @@ class BatchRequestCounts(BaseModel): + """The request counts for different statuses within the batch.""" + completed: int """Number of requests that have been completed successfully.""" diff --git a/src/openai/types/batch_usage.py b/src/openai/types/batch_usage.py index 578f64a5e2..d68d7110ac 100644 --- a/src/openai/types/batch_usage.py +++ b/src/openai/types/batch_usage.py @@ -6,6 +6,8 @@ class InputTokensDetails(BaseModel): + """A detailed breakdown of the input tokens.""" + cached_tokens: int """The number of tokens that were retrieved from the cache. @@ -14,11 +16,19 @@ class InputTokensDetails(BaseModel): class OutputTokensDetails(BaseModel): + """A detailed breakdown of the output tokens.""" + reasoning_tokens: int """The number of reasoning tokens.""" class BatchUsage(BaseModel): + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. Only populated on + batches created after September 7, 2025. + """ + input_tokens: int """The number of input tokens.""" diff --git a/src/openai/types/beta/assistant.py b/src/openai/types/beta/assistant.py index 58421e0f66..61344f85a1 100644 --- a/src/openai/types/beta/assistant.py +++ b/src/openai/types/beta/assistant.py @@ -31,12 +31,19 @@ class ToolResourcesFileSearch(BaseModel): class ToolResources(BaseModel): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: Optional[ToolResourcesCodeInterpreter] = None file_search: Optional[ToolResourcesFileSearch] = None class Assistant(BaseModel): + """Represents an `assistant` that can call the model and use tools.""" + id: str """The identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 38b30f212f..49e7af2d67 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -141,6 +141,11 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + type: Required[Literal["auto"]] """Always `auto`.""" @@ -216,6 +221,11 @@ class ToolResourcesFileSearch(TypedDict, total=False): class ToolResources(TypedDict, total=False): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: ToolResourcesCodeInterpreter file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/assistant_stream_event.py b/src/openai/types/beta/assistant_stream_event.py index 41d3a0c5ea..87620a11d0 100644 --- a/src/openai/types/beta/assistant_stream_event.py +++ b/src/openai/types/beta/assistant_stream_event.py @@ -43,6 +43,10 @@ class ThreadCreated(BaseModel): + """ + Occurs when a new [thread](https://platform.openai.com/docs/api-reference/threads/object) is created. + """ + data: Thread """ Represents a thread that contains @@ -56,6 +60,10 @@ class ThreadCreated(BaseModel): class ThreadRunCreated(BaseModel): + """ + Occurs when a new [run](https://platform.openai.com/docs/api-reference/runs/object) is created. + """ + data: Run """ Represents an execution run on a @@ -66,6 +74,10 @@ class ThreadRunCreated(BaseModel): class ThreadRunQueued(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `queued` status. + """ + data: Run """ Represents an execution run on a @@ -76,6 +88,10 @@ class ThreadRunQueued(BaseModel): class ThreadRunInProgress(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to an `in_progress` status. + """ + data: Run """ Represents an execution run on a @@ -86,6 +102,10 @@ class ThreadRunInProgress(BaseModel): class ThreadRunRequiresAction(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `requires_action` status. + """ + data: Run """ Represents an execution run on a @@ -96,6 +116,10 @@ class ThreadRunRequiresAction(BaseModel): class ThreadRunCompleted(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) is completed. + """ + data: Run """ Represents an execution run on a @@ -106,6 +130,10 @@ class ThreadRunCompleted(BaseModel): class ThreadRunIncomplete(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) ends with status `incomplete`. + """ + data: Run """ Represents an execution run on a @@ -116,6 +144,10 @@ class ThreadRunIncomplete(BaseModel): class ThreadRunFailed(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) fails. + """ + data: Run """ Represents an execution run on a @@ -126,6 +158,10 @@ class ThreadRunFailed(BaseModel): class ThreadRunCancelling(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) moves to a `cancelling` status. + """ + data: Run """ Represents an execution run on a @@ -136,6 +172,10 @@ class ThreadRunCancelling(BaseModel): class ThreadRunCancelled(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) is cancelled. + """ + data: Run """ Represents an execution run on a @@ -146,6 +186,10 @@ class ThreadRunCancelled(BaseModel): class ThreadRunExpired(BaseModel): + """ + Occurs when a [run](https://platform.openai.com/docs/api-reference/runs/object) expires. + """ + data: Run """ Represents an execution run on a @@ -156,6 +200,10 @@ class ThreadRunExpired(BaseModel): class ThreadRunStepCreated(BaseModel): + """ + Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is created. + """ + data: RunStep """Represents a step in execution of a run.""" @@ -163,6 +211,10 @@ class ThreadRunStepCreated(BaseModel): class ThreadRunStepInProgress(BaseModel): + """ + Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) moves to an `in_progress` state. + """ + data: RunStep """Represents a step in execution of a run.""" @@ -170,6 +222,10 @@ class ThreadRunStepInProgress(BaseModel): class ThreadRunStepDelta(BaseModel): + """ + Occurs when parts of a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) are being streamed. + """ + data: RunStepDeltaEvent """Represents a run step delta i.e. @@ -180,6 +236,10 @@ class ThreadRunStepDelta(BaseModel): class ThreadRunStepCompleted(BaseModel): + """ + Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is completed. + """ + data: RunStep """Represents a step in execution of a run.""" @@ -187,6 +247,10 @@ class ThreadRunStepCompleted(BaseModel): class ThreadRunStepFailed(BaseModel): + """ + Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) fails. + """ + data: RunStep """Represents a step in execution of a run.""" @@ -194,6 +258,10 @@ class ThreadRunStepFailed(BaseModel): class ThreadRunStepCancelled(BaseModel): + """ + Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) is cancelled. + """ + data: RunStep """Represents a step in execution of a run.""" @@ -201,6 +269,10 @@ class ThreadRunStepCancelled(BaseModel): class ThreadRunStepExpired(BaseModel): + """ + Occurs when a [run step](https://platform.openai.com/docs/api-reference/run-steps/step-object) expires. + """ + data: RunStep """Represents a step in execution of a run.""" @@ -208,6 +280,10 @@ class ThreadRunStepExpired(BaseModel): class ThreadMessageCreated(BaseModel): + """ + Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) is created. + """ + data: Message """ Represents a message within a @@ -218,6 +294,10 @@ class ThreadMessageCreated(BaseModel): class ThreadMessageInProgress(BaseModel): + """ + Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) moves to an `in_progress` state. + """ + data: Message """ Represents a message within a @@ -228,6 +308,10 @@ class ThreadMessageInProgress(BaseModel): class ThreadMessageDelta(BaseModel): + """ + Occurs when parts of a [Message](https://platform.openai.com/docs/api-reference/messages/object) are being streamed. + """ + data: MessageDeltaEvent """Represents a message delta i.e. @@ -238,6 +322,10 @@ class ThreadMessageDelta(BaseModel): class ThreadMessageCompleted(BaseModel): + """ + Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) is completed. + """ + data: Message """ Represents a message within a @@ -248,6 +336,10 @@ class ThreadMessageCompleted(BaseModel): class ThreadMessageIncomplete(BaseModel): + """ + Occurs when a [message](https://platform.openai.com/docs/api-reference/messages/object) ends before it is completed. + """ + data: Message """ Represents a message within a @@ -258,6 +350,10 @@ class ThreadMessageIncomplete(BaseModel): class ErrorEvent(BaseModel): + """ + Occurs when an [error](https://platform.openai.com/docs/guides/error-codes#api-errors) occurs. This can happen due to an internal server error or a timeout. + """ + data: ErrorObject event: Literal["error"] diff --git a/src/openai/types/beta/assistant_tool_choice.py b/src/openai/types/beta/assistant_tool_choice.py index d73439f006..cabded0b3c 100644 --- a/src/openai/types/beta/assistant_tool_choice.py +++ b/src/openai/types/beta/assistant_tool_choice.py @@ -10,6 +10,11 @@ class AssistantToolChoice(BaseModel): + """Specifies a tool the model should use. + + Use to force the model to call a specific tool. + """ + type: Literal["function", "code_interpreter", "file_search"] """The type of the tool. If type is `function`, the function name must be set""" diff --git a/src/openai/types/beta/assistant_tool_choice_param.py b/src/openai/types/beta/assistant_tool_choice_param.py index 904f489e26..05916bb668 100644 --- a/src/openai/types/beta/assistant_tool_choice_param.py +++ b/src/openai/types/beta/assistant_tool_choice_param.py @@ -10,6 +10,11 @@ class AssistantToolChoiceParam(TypedDict, total=False): + """Specifies a tool the model should use. + + Use to force the model to call a specific tool. + """ + type: Required[Literal["function", "code_interpreter", "file_search"]] """The type of the tool. If type is `function`, the function name must be set""" diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 8f774c4e6c..d84b15cc5b 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -187,6 +187,11 @@ class ToolResourcesFileSearch(TypedDict, total=False): class ToolResources(TypedDict, total=False): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: ToolResourcesCodeInterpreter file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/chatkit/chat_session.py b/src/openai/types/beta/chatkit/chat_session.py index 82baea211c..9db9fc93a0 100644 --- a/src/openai/types/beta/chatkit/chat_session.py +++ b/src/openai/types/beta/chatkit/chat_session.py @@ -12,6 +12,8 @@ class ChatSession(BaseModel): + """Represents a ChatKit session and its resolved configuration.""" + id: str """Identifier for the ChatKit session.""" diff --git a/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py b/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py index 4fa96a4433..1d95255e06 100644 --- a/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py +++ b/src/openai/types/beta/chatkit/chat_session_automatic_thread_titling.py @@ -6,5 +6,7 @@ class ChatSessionAutomaticThreadTitling(BaseModel): + """Automatic thread title preferences for the session.""" + enabled: bool """Whether automatic thread titling is enabled.""" diff --git a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py index 6205b172cf..f9fa0ceff5 100644 --- a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py +++ b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration.py @@ -9,6 +9,8 @@ class ChatSessionChatKitConfiguration(BaseModel): + """ChatKit configuration for the session.""" + automatic_thread_titling: ChatSessionAutomaticThreadTitling """Automatic thread titling preferences.""" diff --git a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py index 0a5ae80a76..834de71e71 100644 --- a/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py +++ b/src/openai/types/beta/chatkit/chat_session_chatkit_configuration_param.py @@ -8,11 +8,21 @@ class AutomaticThreadTitling(TypedDict, total=False): + """Configuration for automatic thread titling. + + When omitted, automatic thread titling is enabled by default. + """ + enabled: bool """Enable automatic thread title generation. Defaults to true.""" class FileUpload(TypedDict, total=False): + """Configuration for upload enablement and limits. + + When omitted, uploads are disabled by default (max_files 10, max_file_size 512 MB). + """ + enabled: bool """Enable uploads for this session. Defaults to false.""" @@ -27,6 +37,11 @@ class FileUpload(TypedDict, total=False): class History(TypedDict, total=False): + """Configuration for chat history retention. + + When omitted, history is enabled by default with no limit on recent_threads (null). + """ + enabled: bool """Enables chat users to access previous ChatKit threads. Defaults to true.""" @@ -38,6 +53,8 @@ class History(TypedDict, total=False): class ChatSessionChatKitConfigurationParam(TypedDict, total=False): + """Optional per-session configuration settings for ChatKit behavior.""" + automatic_thread_titling: AutomaticThreadTitling """Configuration for automatic thread titling. diff --git a/src/openai/types/beta/chatkit/chat_session_expires_after_param.py b/src/openai/types/beta/chatkit/chat_session_expires_after_param.py index ceb5a984c5..c1de8a767a 100644 --- a/src/openai/types/beta/chatkit/chat_session_expires_after_param.py +++ b/src/openai/types/beta/chatkit/chat_session_expires_after_param.py @@ -8,6 +8,8 @@ class ChatSessionExpiresAfterParam(TypedDict, total=False): + """Controls when the session expires relative to an anchor timestamp.""" + anchor: Required[Literal["created_at"]] """Base timestamp used to calculate expiration. Currently fixed to `created_at`.""" diff --git a/src/openai/types/beta/chatkit/chat_session_file_upload.py b/src/openai/types/beta/chatkit/chat_session_file_upload.py index c63c7a0149..0275859d27 100644 --- a/src/openai/types/beta/chatkit/chat_session_file_upload.py +++ b/src/openai/types/beta/chatkit/chat_session_file_upload.py @@ -8,6 +8,8 @@ class ChatSessionFileUpload(BaseModel): + """Upload permissions and limits applied to the session.""" + enabled: bool """Indicates if uploads are enabled for the session.""" diff --git a/src/openai/types/beta/chatkit/chat_session_history.py b/src/openai/types/beta/chatkit/chat_session_history.py index 66ebe00877..54690009c2 100644 --- a/src/openai/types/beta/chatkit/chat_session_history.py +++ b/src/openai/types/beta/chatkit/chat_session_history.py @@ -8,6 +8,8 @@ class ChatSessionHistory(BaseModel): + """History retention preferences returned for the session.""" + enabled: bool """Indicates if chat history is persisted for the session.""" diff --git a/src/openai/types/beta/chatkit/chat_session_rate_limits.py b/src/openai/types/beta/chatkit/chat_session_rate_limits.py index 392225e347..7c5bd94e76 100644 --- a/src/openai/types/beta/chatkit/chat_session_rate_limits.py +++ b/src/openai/types/beta/chatkit/chat_session_rate_limits.py @@ -6,5 +6,7 @@ class ChatSessionRateLimits(BaseModel): + """Active per-minute request limit for the session.""" + max_requests_per_1_minute: int """Maximum allowed requests per one-minute window.""" diff --git a/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py b/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py index 7894c06484..578f20b0c3 100644 --- a/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py +++ b/src/openai/types/beta/chatkit/chat_session_rate_limits_param.py @@ -8,5 +8,7 @@ class ChatSessionRateLimitsParam(TypedDict, total=False): + """Controls request rate limits for the session.""" + max_requests_per_1_minute: int """Maximum number of requests allowed per minute for the session. Defaults to 10.""" diff --git a/src/openai/types/beta/chatkit/chat_session_workflow_param.py b/src/openai/types/beta/chatkit/chat_session_workflow_param.py index 5542922102..abf52de526 100644 --- a/src/openai/types/beta/chatkit/chat_session_workflow_param.py +++ b/src/openai/types/beta/chatkit/chat_session_workflow_param.py @@ -9,11 +9,18 @@ class Tracing(TypedDict, total=False): + """Optional tracing overrides for the workflow invocation. + + When omitted, tracing is enabled by default. + """ + enabled: bool """Whether tracing is enabled during the session. Defaults to true.""" class ChatSessionWorkflowParam(TypedDict, total=False): + """Workflow reference and overrides applied to the chat session.""" + id: Required[str] """Identifier for the workflow invoked by the session.""" diff --git a/src/openai/types/beta/chatkit/chatkit_attachment.py b/src/openai/types/beta/chatkit/chatkit_attachment.py index 8d8ad3e128..7750925e03 100644 --- a/src/openai/types/beta/chatkit/chatkit_attachment.py +++ b/src/openai/types/beta/chatkit/chatkit_attachment.py @@ -9,6 +9,8 @@ class ChatKitAttachment(BaseModel): + """Attachment metadata included on thread items.""" + id: str """Identifier for the attachment.""" diff --git a/src/openai/types/beta/chatkit/chatkit_response_output_text.py b/src/openai/types/beta/chatkit/chatkit_response_output_text.py index 116b797ec2..1348fed2b2 100644 --- a/src/openai/types/beta/chatkit/chatkit_response_output_text.py +++ b/src/openai/types/beta/chatkit/chatkit_response_output_text.py @@ -17,6 +17,8 @@ class AnnotationFileSource(BaseModel): + """File attachment referenced by the annotation.""" + filename: str """Filename referenced by the annotation.""" @@ -25,6 +27,8 @@ class AnnotationFileSource(BaseModel): class AnnotationFile(BaseModel): + """Annotation that references an uploaded file.""" + source: AnnotationFileSource """File attachment referenced by the annotation.""" @@ -33,6 +37,8 @@ class AnnotationFile(BaseModel): class AnnotationURLSource(BaseModel): + """URL referenced by the annotation.""" + type: Literal["url"] """Type discriminator that is always `url`.""" @@ -41,6 +47,8 @@ class AnnotationURLSource(BaseModel): class AnnotationURL(BaseModel): + """Annotation that references a URL.""" + source: AnnotationURLSource """URL referenced by the annotation.""" @@ -52,6 +60,8 @@ class AnnotationURL(BaseModel): class ChatKitResponseOutputText(BaseModel): + """Assistant response text accompanied by optional annotations.""" + annotations: List[Annotation] """Ordered list of annotations attached to the response text.""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread.py b/src/openai/types/beta/chatkit/chatkit_thread.py index abd1a9ea01..32075233d8 100644 --- a/src/openai/types/beta/chatkit/chatkit_thread.py +++ b/src/openai/types/beta/chatkit/chatkit_thread.py @@ -10,11 +10,15 @@ class StatusActive(BaseModel): + """Indicates that a thread is active.""" + type: Literal["active"] """Status discriminator that is always `active`.""" class StatusLocked(BaseModel): + """Indicates that a thread is locked and cannot accept new input.""" + reason: Optional[str] = None """Reason that the thread was locked. Defaults to null when no reason is recorded.""" @@ -23,6 +27,8 @@ class StatusLocked(BaseModel): class StatusClosed(BaseModel): + """Indicates that a thread has been closed.""" + reason: Optional[str] = None """Reason that the thread was closed. Defaults to null when no reason is recorded.""" @@ -34,6 +40,8 @@ class StatusClosed(BaseModel): class ChatKitThread(BaseModel): + """Represents a ChatKit thread and its current status.""" + id: str """Identifier of the thread.""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py b/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py index f4afd053b6..337f53a83d 100644 --- a/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py +++ b/src/openai/types/beta/chatkit/chatkit_thread_assistant_message_item.py @@ -10,6 +10,8 @@ class ChatKitThreadAssistantMessageItem(BaseModel): + """Assistant-authored message within a thread.""" + id: str """Identifier of the thread item.""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread_item_list.py b/src/openai/types/beta/chatkit/chatkit_thread_item_list.py index 173bd15055..049ca54429 100644 --- a/src/openai/types/beta/chatkit/chatkit_thread_item_list.py +++ b/src/openai/types/beta/chatkit/chatkit_thread_item_list.py @@ -20,6 +20,8 @@ class DataChatKitClientToolCall(BaseModel): + """Record of a client side tool invocation initiated by the assistant.""" + id: str """Identifier of the thread item.""" @@ -55,6 +57,8 @@ class DataChatKitClientToolCall(BaseModel): class DataChatKitTask(BaseModel): + """Task emitted by the workflow to show progress and status updates.""" + id: str """Identifier of the thread item.""" @@ -81,6 +85,8 @@ class DataChatKitTask(BaseModel): class DataChatKitTaskGroupTask(BaseModel): + """Task entry that appears within a TaskGroup.""" + heading: Optional[str] = None """Optional heading for the grouped task. Defaults to null when not provided.""" @@ -95,6 +101,8 @@ class DataChatKitTaskGroupTask(BaseModel): class DataChatKitTaskGroup(BaseModel): + """Collection of workflow tasks grouped together in the thread.""" + id: str """Identifier of the thread item.""" @@ -128,6 +136,8 @@ class DataChatKitTaskGroup(BaseModel): class ChatKitThreadItemList(BaseModel): + """A paginated list of thread items rendered for the ChatKit API.""" + data: List[Data] """A list of items""" diff --git a/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py b/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py index 233d07232f..d7552c4f2e 100644 --- a/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py +++ b/src/openai/types/beta/chatkit/chatkit_thread_user_message_item.py @@ -18,6 +18,8 @@ class ContentInputText(BaseModel): + """Text block that a user contributed to the thread.""" + text: str """Plain-text content supplied by the user.""" @@ -26,6 +28,8 @@ class ContentInputText(BaseModel): class ContentQuotedText(BaseModel): + """Quoted snippet that the user referenced in their message.""" + text: str """Quoted text content.""" @@ -37,11 +41,15 @@ class ContentQuotedText(BaseModel): class InferenceOptionsToolChoice(BaseModel): + """Preferred tool to invoke. Defaults to null when ChatKit should auto-select.""" + id: str """Identifier of the requested tool.""" class InferenceOptions(BaseModel): + """Inference overrides applied to the message. Defaults to null when unset.""" + model: Optional[str] = None """Model name that generated the response. @@ -53,6 +61,8 @@ class InferenceOptions(BaseModel): class ChatKitThreadUserMessageItem(BaseModel): + """User-authored messages within a thread.""" + id: str """Identifier of the thread item.""" diff --git a/src/openai/types/beta/chatkit/chatkit_widget_item.py b/src/openai/types/beta/chatkit/chatkit_widget_item.py index c7f182259a..a269c736fb 100644 --- a/src/openai/types/beta/chatkit/chatkit_widget_item.py +++ b/src/openai/types/beta/chatkit/chatkit_widget_item.py @@ -8,6 +8,8 @@ class ChatKitWidgetItem(BaseModel): + """Thread item that renders a widget payload.""" + id: str """Identifier of the thread item.""" diff --git a/src/openai/types/beta/chatkit/thread_delete_response.py b/src/openai/types/beta/chatkit/thread_delete_response.py index 03fdec9c2c..45b686bf8b 100644 --- a/src/openai/types/beta/chatkit/thread_delete_response.py +++ b/src/openai/types/beta/chatkit/thread_delete_response.py @@ -8,6 +8,8 @@ class ThreadDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a thread.""" + id: str """Identifier of the deleted thread.""" diff --git a/src/openai/types/beta/chatkit_workflow.py b/src/openai/types/beta/chatkit_workflow.py index 00fbcf41ce..b6f5b55b4a 100644 --- a/src/openai/types/beta/chatkit_workflow.py +++ b/src/openai/types/beta/chatkit_workflow.py @@ -8,11 +8,15 @@ class Tracing(BaseModel): + """Tracing settings applied to the workflow.""" + enabled: bool """Indicates whether tracing is enabled.""" class ChatKitWorkflow(BaseModel): + """Workflow metadata and state returned for the session.""" + id: str """Identifier of the workflow backing the session.""" diff --git a/src/openai/types/beta/file_search_tool.py b/src/openai/types/beta/file_search_tool.py index 89fc16c04c..9e33249e0b 100644 --- a/src/openai/types/beta/file_search_tool.py +++ b/src/openai/types/beta/file_search_tool.py @@ -9,6 +9,13 @@ class FileSearchRankingOptions(BaseModel): + """The ranking options for the file search. + + If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0. + + See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. + """ + score_threshold: float """The score threshold for the file search. @@ -23,6 +30,8 @@ class FileSearchRankingOptions(BaseModel): class FileSearch(BaseModel): + """Overrides for the file search tool.""" + max_num_results: Optional[int] = None """The maximum number of results the file search tool should output. diff --git a/src/openai/types/beta/file_search_tool_param.py b/src/openai/types/beta/file_search_tool_param.py index c73d0af79d..9906b4b2a4 100644 --- a/src/openai/types/beta/file_search_tool_param.py +++ b/src/openai/types/beta/file_search_tool_param.py @@ -8,6 +8,13 @@ class FileSearchRankingOptions(TypedDict, total=False): + """The ranking options for the file search. + + If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0. + + See the [file search tool documentation](https://platform.openai.com/docs/assistants/tools/file-search#customizing-file-search-settings) for more information. + """ + score_threshold: Required[float] """The score threshold for the file search. @@ -22,6 +29,8 @@ class FileSearchRankingOptions(TypedDict, total=False): class FileSearch(TypedDict, total=False): + """Overrides for the file search tool.""" + max_num_results: int """The maximum number of results the file search tool should output. diff --git a/src/openai/types/beta/thread.py b/src/openai/types/beta/thread.py index 789f66e48b..83d9055194 100644 --- a/src/openai/types/beta/thread.py +++ b/src/openai/types/beta/thread.py @@ -29,12 +29,20 @@ class ToolResourcesFileSearch(BaseModel): class ToolResources(BaseModel): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: Optional[ToolResourcesCodeInterpreter] = None file_search: Optional[ToolResourcesFileSearch] = None class Thread(BaseModel): + """ + Represents a thread that contains [messages](https://platform.openai.com/docs/api-reference/messages). + """ + id: str """The identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index 734e5e2a4e..c0aee3e9f8 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -227,6 +227,11 @@ class ThreadToolResourcesCodeInterpreter(TypedDict, total=False): class ThreadToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + type: Required[Literal["auto"]] """Always `auto`.""" @@ -303,12 +308,22 @@ class ThreadToolResourcesFileSearch(TypedDict, total=False): class ThreadToolResources(TypedDict, total=False): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: ThreadToolResourcesCodeInterpreter file_search: ThreadToolResourcesFileSearch class Thread(TypedDict, total=False): + """Options to create a new thread. + + If no thread is provided when running a + request, an empty thread will be created. + """ + messages: Iterable[ThreadMessage] """ A list of [messages](https://platform.openai.com/docs/api-reference/messages) to @@ -354,12 +369,22 @@ class ToolResourcesFileSearch(TypedDict, total=False): class ToolResources(TypedDict, total=False): + """A set of resources that are used by the assistant's tools. + + The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: ToolResourcesCodeInterpreter file_search: ToolResourcesFileSearch class TruncationStrategy(TypedDict, total=False): + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + type: Required[Literal["auto", "last_messages"]] """The truncation strategy to use for the thread. diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index 8fd9f38df7..ef83e3d465 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -106,6 +106,11 @@ class ToolResourcesCodeInterpreter(TypedDict, total=False): class ToolResourcesFileSearchVectorStoreChunkingStrategyAuto(TypedDict, total=False): + """The default strategy. + + This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + """ + type: Required[Literal["auto"]] """Always `auto`.""" @@ -181,6 +186,10 @@ class ToolResourcesFileSearch(TypedDict, total=False): class ToolResources(TypedDict, total=False): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: ToolResourcesCodeInterpreter file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/thread_update_params.py b/src/openai/types/beta/thread_update_params.py index 464ea8d7eb..e000edc05f 100644 --- a/src/openai/types/beta/thread_update_params.py +++ b/src/openai/types/beta/thread_update_params.py @@ -51,6 +51,10 @@ class ToolResourcesFileSearch(TypedDict, total=False): class ToolResources(TypedDict, total=False): + """ + A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs. + """ + code_interpreter: ToolResourcesCodeInterpreter file_search: ToolResourcesFileSearch diff --git a/src/openai/types/beta/threads/file_citation_annotation.py b/src/openai/types/beta/threads/file_citation_annotation.py index c3085aed9b..929da0ac56 100644 --- a/src/openai/types/beta/threads/file_citation_annotation.py +++ b/src/openai/types/beta/threads/file_citation_annotation.py @@ -13,6 +13,10 @@ class FileCitation(BaseModel): class FileCitationAnnotation(BaseModel): + """ + A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files. + """ + end_index: int file_citation: FileCitation diff --git a/src/openai/types/beta/threads/file_citation_delta_annotation.py b/src/openai/types/beta/threads/file_citation_delta_annotation.py index b40c0d123e..591e322332 100644 --- a/src/openai/types/beta/threads/file_citation_delta_annotation.py +++ b/src/openai/types/beta/threads/file_citation_delta_annotation.py @@ -17,6 +17,10 @@ class FileCitation(BaseModel): class FileCitationDeltaAnnotation(BaseModel): + """ + A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files. + """ + index: int """The index of the annotation in the text content part.""" diff --git a/src/openai/types/beta/threads/file_path_annotation.py b/src/openai/types/beta/threads/file_path_annotation.py index 9812737ece..d3c144c2fc 100644 --- a/src/openai/types/beta/threads/file_path_annotation.py +++ b/src/openai/types/beta/threads/file_path_annotation.py @@ -13,6 +13,10 @@ class FilePath(BaseModel): class FilePathAnnotation(BaseModel): + """ + A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + """ + end_index: int file_path: FilePath diff --git a/src/openai/types/beta/threads/file_path_delta_annotation.py b/src/openai/types/beta/threads/file_path_delta_annotation.py index 0cbb445e48..5416874749 100644 --- a/src/openai/types/beta/threads/file_path_delta_annotation.py +++ b/src/openai/types/beta/threads/file_path_delta_annotation.py @@ -14,6 +14,10 @@ class FilePath(BaseModel): class FilePathDeltaAnnotation(BaseModel): + """ + A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + """ + index: int """The index of the annotation in the text content part.""" diff --git a/src/openai/types/beta/threads/image_file_content_block.py b/src/openai/types/beta/threads/image_file_content_block.py index a909999065..5a082cd488 100644 --- a/src/openai/types/beta/threads/image_file_content_block.py +++ b/src/openai/types/beta/threads/image_file_content_block.py @@ -9,6 +9,10 @@ class ImageFileContentBlock(BaseModel): + """ + References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message. + """ + image_file: ImageFile type: Literal["image_file"] diff --git a/src/openai/types/beta/threads/image_file_content_block_param.py b/src/openai/types/beta/threads/image_file_content_block_param.py index 48d94bee36..da095a5ff6 100644 --- a/src/openai/types/beta/threads/image_file_content_block_param.py +++ b/src/openai/types/beta/threads/image_file_content_block_param.py @@ -10,6 +10,10 @@ class ImageFileContentBlockParam(TypedDict, total=False): + """ + References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message. + """ + image_file: Required[ImageFileParam] type: Required[Literal["image_file"]] diff --git a/src/openai/types/beta/threads/image_file_delta_block.py b/src/openai/types/beta/threads/image_file_delta_block.py index 0a5a2e8a5f..ed17f7ff3b 100644 --- a/src/openai/types/beta/threads/image_file_delta_block.py +++ b/src/openai/types/beta/threads/image_file_delta_block.py @@ -10,6 +10,10 @@ class ImageFileDeltaBlock(BaseModel): + """ + References an image [File](https://platform.openai.com/docs/api-reference/files) in the content of a message. + """ + index: int """The index of the content part in the message.""" diff --git a/src/openai/types/beta/threads/image_url_content_block.py b/src/openai/types/beta/threads/image_url_content_block.py index 40a16c1df8..8dc1f16a7a 100644 --- a/src/openai/types/beta/threads/image_url_content_block.py +++ b/src/openai/types/beta/threads/image_url_content_block.py @@ -9,6 +9,8 @@ class ImageURLContentBlock(BaseModel): + """References an image URL in the content of a message.""" + image_url: ImageURL type: Literal["image_url"] diff --git a/src/openai/types/beta/threads/image_url_content_block_param.py b/src/openai/types/beta/threads/image_url_content_block_param.py index 585b926c58..a5c59e02c2 100644 --- a/src/openai/types/beta/threads/image_url_content_block_param.py +++ b/src/openai/types/beta/threads/image_url_content_block_param.py @@ -10,6 +10,8 @@ class ImageURLContentBlockParam(TypedDict, total=False): + """References an image URL in the content of a message.""" + image_url: Required[ImageURLParam] type: Required[Literal["image_url"]] diff --git a/src/openai/types/beta/threads/image_url_delta_block.py b/src/openai/types/beta/threads/image_url_delta_block.py index 5252da12dd..3128d8e709 100644 --- a/src/openai/types/beta/threads/image_url_delta_block.py +++ b/src/openai/types/beta/threads/image_url_delta_block.py @@ -10,6 +10,8 @@ class ImageURLDeltaBlock(BaseModel): + """References an image URL in the content of a message.""" + index: int """The index of the content part in the message.""" diff --git a/src/openai/types/beta/threads/message.py b/src/openai/types/beta/threads/message.py index 4a05a128eb..fc7f73f091 100644 --- a/src/openai/types/beta/threads/message.py +++ b/src/openai/types/beta/threads/message.py @@ -34,11 +34,17 @@ class Attachment(BaseModel): class IncompleteDetails(BaseModel): + """On an incomplete message, details about why the message is incomplete.""" + reason: Literal["content_filter", "max_tokens", "run_cancelled", "run_expired", "run_failed"] """The reason the message is incomplete.""" class Message(BaseModel): + """ + Represents a message within a [thread](https://platform.openai.com/docs/api-reference/threads). + """ + id: str """The identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/beta/threads/message_delta.py b/src/openai/types/beta/threads/message_delta.py index ecd0dfe319..fdeebb3a12 100644 --- a/src/openai/types/beta/threads/message_delta.py +++ b/src/openai/types/beta/threads/message_delta.py @@ -10,6 +10,8 @@ class MessageDelta(BaseModel): + """The delta containing the fields that have changed on the Message.""" + content: Optional[List[MessageContentDelta]] = None """The content of the message in array of text and/or images.""" diff --git a/src/openai/types/beta/threads/message_delta_event.py b/src/openai/types/beta/threads/message_delta_event.py index 3811cef679..d5ba1e172d 100644 --- a/src/openai/types/beta/threads/message_delta_event.py +++ b/src/openai/types/beta/threads/message_delta_event.py @@ -9,6 +9,11 @@ class MessageDeltaEvent(BaseModel): + """Represents a message delta i.e. + + any changed fields on a message during streaming. + """ + id: str """The identifier of the message, which can be referenced in API endpoints.""" diff --git a/src/openai/types/beta/threads/refusal_content_block.py b/src/openai/types/beta/threads/refusal_content_block.py index d54f948554..b4512b3ccb 100644 --- a/src/openai/types/beta/threads/refusal_content_block.py +++ b/src/openai/types/beta/threads/refusal_content_block.py @@ -8,6 +8,8 @@ class RefusalContentBlock(BaseModel): + """The refusal content generated by the assistant.""" + refusal: str type: Literal["refusal"] diff --git a/src/openai/types/beta/threads/refusal_delta_block.py b/src/openai/types/beta/threads/refusal_delta_block.py index dbd8e62697..85a1f08db1 100644 --- a/src/openai/types/beta/threads/refusal_delta_block.py +++ b/src/openai/types/beta/threads/refusal_delta_block.py @@ -9,6 +9,8 @@ class RefusalDeltaBlock(BaseModel): + """The refusal content that is part of a message.""" + index: int """The index of the refusal part in the message.""" diff --git a/src/openai/types/beta/threads/required_action_function_tool_call.py b/src/openai/types/beta/threads/required_action_function_tool_call.py index a24dfd068b..3cec8514ca 100644 --- a/src/openai/types/beta/threads/required_action_function_tool_call.py +++ b/src/openai/types/beta/threads/required_action_function_tool_call.py @@ -8,6 +8,8 @@ class Function(BaseModel): + """The function definition.""" + arguments: str """The arguments that the model expects you to pass to the function.""" @@ -16,6 +18,8 @@ class Function(BaseModel): class RequiredActionFunctionToolCall(BaseModel): + """Tool call objects""" + id: str """The ID of the tool call. diff --git a/src/openai/types/beta/threads/run.py b/src/openai/types/beta/threads/run.py index c545cc3759..8a88fa1673 100644 --- a/src/openai/types/beta/threads/run.py +++ b/src/openai/types/beta/threads/run.py @@ -23,6 +23,11 @@ class IncompleteDetails(BaseModel): + """Details on why the run is incomplete. + + Will be `null` if the run is not incomplete. + """ + reason: Optional[Literal["max_completion_tokens", "max_prompt_tokens"]] = None """The reason why the run is incomplete. @@ -32,6 +37,8 @@ class IncompleteDetails(BaseModel): class LastError(BaseModel): + """The last error associated with this run. Will be `null` if there are no errors.""" + code: Literal["server_error", "rate_limit_exceeded", "invalid_prompt"] """One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`.""" @@ -40,11 +47,18 @@ class LastError(BaseModel): class RequiredActionSubmitToolOutputs(BaseModel): + """Details on the tool outputs needed for this run to continue.""" + tool_calls: List[RequiredActionFunctionToolCall] """A list of the relevant tool calls.""" class RequiredAction(BaseModel): + """Details on the action required to continue the run. + + Will be `null` if no action is required. + """ + submit_tool_outputs: RequiredActionSubmitToolOutputs """Details on the tool outputs needed for this run to continue.""" @@ -53,6 +67,11 @@ class RequiredAction(BaseModel): class TruncationStrategy(BaseModel): + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + type: Literal["auto", "last_messages"] """The truncation strategy to use for the thread. @@ -70,6 +89,11 @@ class TruncationStrategy(BaseModel): class Usage(BaseModel): + """Usage statistics related to the run. + + This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.). + """ + completion_tokens: int """Number of completion tokens used over the course of the run.""" @@ -81,6 +105,10 @@ class Usage(BaseModel): class Run(BaseModel): + """ + Represents an execution run on a [thread](https://platform.openai.com/docs/api-reference/threads). + """ + id: str """The identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index df789decbc..f4c56feb56 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -232,6 +232,11 @@ class AdditionalMessage(TypedDict, total=False): class TruncationStrategy(TypedDict, total=False): + """Controls for how a thread will be truncated prior to the run. + + Use this to control the initial context window of the run. + """ + type: Required[Literal["auto", "last_messages"]] """The truncation strategy to use for the thread. diff --git a/src/openai/types/beta/threads/runs/code_interpreter_logs.py b/src/openai/types/beta/threads/runs/code_interpreter_logs.py index 0bf8c1dac2..722fd2b4c4 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_logs.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_logs.py @@ -9,6 +9,8 @@ class CodeInterpreterLogs(BaseModel): + """Text output from the Code Interpreter tool call as part of a run step.""" + index: int """The index of the output in the outputs array.""" diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py index e7df4e19c4..bc78b5fa3d 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call.py @@ -17,6 +17,8 @@ class CodeInterpreterOutputLogs(BaseModel): + """Text output from the Code Interpreter tool call as part of a run step.""" + logs: str """The text output from the Code Interpreter tool call.""" @@ -45,6 +47,8 @@ class CodeInterpreterOutputImage(BaseModel): class CodeInterpreter(BaseModel): + """The Code Interpreter tool call definition.""" + input: str """The input to the Code Interpreter tool call.""" @@ -57,6 +61,8 @@ class CodeInterpreter(BaseModel): class CodeInterpreterToolCall(BaseModel): + """Details of the Code Interpreter tool call the run step was involved in.""" + id: str """The ID of the tool call.""" diff --git a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py index 9d7a1563cd..efedac795c 100644 --- a/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/code_interpreter_tool_call_delta.py @@ -16,6 +16,8 @@ class CodeInterpreter(BaseModel): + """The Code Interpreter tool call definition.""" + input: Optional[str] = None """The input to the Code Interpreter tool call.""" @@ -28,6 +30,8 @@ class CodeInterpreter(BaseModel): class CodeInterpreterToolCallDelta(BaseModel): + """Details of the Code Interpreter tool call the run step was involved in.""" + index: int """The index of the tool call in the tool calls array.""" diff --git a/src/openai/types/beta/threads/runs/file_search_tool_call.py b/src/openai/types/beta/threads/runs/file_search_tool_call.py index a2068daad1..291a93ec65 100644 --- a/src/openai/types/beta/threads/runs/file_search_tool_call.py +++ b/src/openai/types/beta/threads/runs/file_search_tool_call.py @@ -15,6 +15,8 @@ class FileSearchRankingOptions(BaseModel): + """The ranking options for the file search.""" + ranker: Literal["auto", "default_2024_08_21"] """The ranker to use for the file search. @@ -37,6 +39,8 @@ class FileSearchResultContent(BaseModel): class FileSearchResult(BaseModel): + """A result instance of the file search.""" + file_id: str """The ID of the file that result was found in.""" @@ -57,6 +61,8 @@ class FileSearchResult(BaseModel): class FileSearch(BaseModel): + """For now, this is always going to be an empty object.""" + ranking_options: Optional[FileSearchRankingOptions] = None """The ranking options for the file search.""" diff --git a/src/openai/types/beta/threads/runs/function_tool_call.py b/src/openai/types/beta/threads/runs/function_tool_call.py index b1d354f894..dd0e22cfb1 100644 --- a/src/openai/types/beta/threads/runs/function_tool_call.py +++ b/src/openai/types/beta/threads/runs/function_tool_call.py @@ -9,6 +9,8 @@ class Function(BaseModel): + """The definition of the function that was called.""" + arguments: str """The arguments passed to the function.""" diff --git a/src/openai/types/beta/threads/runs/function_tool_call_delta.py b/src/openai/types/beta/threads/runs/function_tool_call_delta.py index faaf026f7f..4107e1b873 100644 --- a/src/openai/types/beta/threads/runs/function_tool_call_delta.py +++ b/src/openai/types/beta/threads/runs/function_tool_call_delta.py @@ -9,6 +9,8 @@ class Function(BaseModel): + """The definition of the function that was called.""" + arguments: Optional[str] = None """The arguments passed to the function.""" diff --git a/src/openai/types/beta/threads/runs/message_creation_step_details.py b/src/openai/types/beta/threads/runs/message_creation_step_details.py index 73439079d3..cd925b57ce 100644 --- a/src/openai/types/beta/threads/runs/message_creation_step_details.py +++ b/src/openai/types/beta/threads/runs/message_creation_step_details.py @@ -13,6 +13,8 @@ class MessageCreation(BaseModel): class MessageCreationStepDetails(BaseModel): + """Details of the message creation by the run step.""" + message_creation: MessageCreation type: Literal["message_creation"] diff --git a/src/openai/types/beta/threads/runs/run_step.py b/src/openai/types/beta/threads/runs/run_step.py index b5f380c7b1..97451229fc 100644 --- a/src/openai/types/beta/threads/runs/run_step.py +++ b/src/openai/types/beta/threads/runs/run_step.py @@ -13,6 +13,11 @@ class LastError(BaseModel): + """The last error associated with this run step. + + Will be `null` if there are no errors. + """ + code: Literal["server_error", "rate_limit_exceeded"] """One of `server_error` or `rate_limit_exceeded`.""" @@ -26,6 +31,11 @@ class LastError(BaseModel): class Usage(BaseModel): + """Usage statistics related to the run step. + + This value will be `null` while the run step's status is `in_progress`. + """ + completion_tokens: int """Number of completion tokens used over the course of the run step.""" @@ -37,6 +47,8 @@ class Usage(BaseModel): class RunStep(BaseModel): + """Represents a step in execution of a run.""" + id: str """The identifier of the run step, which can be referenced in API endpoints.""" diff --git a/src/openai/types/beta/threads/runs/run_step_delta.py b/src/openai/types/beta/threads/runs/run_step_delta.py index 1139088fb4..2ccb770d57 100644 --- a/src/openai/types/beta/threads/runs/run_step_delta.py +++ b/src/openai/types/beta/threads/runs/run_step_delta.py @@ -16,5 +16,7 @@ class RunStepDelta(BaseModel): + """The delta containing the fields that have changed on the run step.""" + step_details: Optional[StepDetails] = None """The details of the run step.""" diff --git a/src/openai/types/beta/threads/runs/run_step_delta_event.py b/src/openai/types/beta/threads/runs/run_step_delta_event.py index 7f3f92aabf..8f1c095ae4 100644 --- a/src/openai/types/beta/threads/runs/run_step_delta_event.py +++ b/src/openai/types/beta/threads/runs/run_step_delta_event.py @@ -9,6 +9,11 @@ class RunStepDeltaEvent(BaseModel): + """Represents a run step delta i.e. + + any changed fields on a run step during streaming. + """ + id: str """The identifier of the run step, which can be referenced in API endpoints.""" diff --git a/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py index f58ed3d96d..4b18277c18 100644 --- a/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py +++ b/src/openai/types/beta/threads/runs/run_step_delta_message_delta.py @@ -14,6 +14,8 @@ class MessageCreation(BaseModel): class RunStepDeltaMessageDelta(BaseModel): + """Details of the message creation by the run step.""" + type: Literal["message_creation"] """Always `message_creation`.""" diff --git a/src/openai/types/beta/threads/runs/tool_call_delta_object.py b/src/openai/types/beta/threads/runs/tool_call_delta_object.py index 189dce772c..dbd1096ad6 100644 --- a/src/openai/types/beta/threads/runs/tool_call_delta_object.py +++ b/src/openai/types/beta/threads/runs/tool_call_delta_object.py @@ -10,6 +10,8 @@ class ToolCallDeltaObject(BaseModel): + """Details of the tool call.""" + type: Literal["tool_calls"] """Always `tool_calls`.""" diff --git a/src/openai/types/beta/threads/runs/tool_calls_step_details.py b/src/openai/types/beta/threads/runs/tool_calls_step_details.py index a084d387c7..1f54a6aa71 100644 --- a/src/openai/types/beta/threads/runs/tool_calls_step_details.py +++ b/src/openai/types/beta/threads/runs/tool_calls_step_details.py @@ -10,6 +10,8 @@ class ToolCallsStepDetails(BaseModel): + """Details of the tool call.""" + tool_calls: List[ToolCall] """An array of tool calls the run step was involved in. diff --git a/src/openai/types/beta/threads/text_content_block.py b/src/openai/types/beta/threads/text_content_block.py index 3706d6b9d8..b9b1368a17 100644 --- a/src/openai/types/beta/threads/text_content_block.py +++ b/src/openai/types/beta/threads/text_content_block.py @@ -9,6 +9,8 @@ class TextContentBlock(BaseModel): + """The text content that is part of a message.""" + text: Text type: Literal["text"] diff --git a/src/openai/types/beta/threads/text_content_block_param.py b/src/openai/types/beta/threads/text_content_block_param.py index 6313de32cc..22c864438d 100644 --- a/src/openai/types/beta/threads/text_content_block_param.py +++ b/src/openai/types/beta/threads/text_content_block_param.py @@ -8,6 +8,8 @@ class TextContentBlockParam(TypedDict, total=False): + """The text content that is part of a message.""" + text: Required[str] """Text content to be sent to the model""" diff --git a/src/openai/types/beta/threads/text_delta_block.py b/src/openai/types/beta/threads/text_delta_block.py index 586116e0d6..a3d339ccad 100644 --- a/src/openai/types/beta/threads/text_delta_block.py +++ b/src/openai/types/beta/threads/text_delta_block.py @@ -10,6 +10,8 @@ class TextDeltaBlock(BaseModel): + """The text content that is part of a message.""" + index: int """The index of the content part in the message.""" diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 6bc4bafe79..31219aa812 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -12,6 +12,8 @@ class ChoiceLogprobs(BaseModel): + """Log probability information for the choice.""" + content: Optional[List[ChatCompletionTokenLogprob]] = None """A list of message content tokens with log probability information.""" @@ -41,6 +43,10 @@ class Choice(BaseModel): class ChatCompletion(BaseModel): + """ + Represents a chat completion response returned by model, based on the provided input. + """ + id: str """A unique identifier for the chat completion.""" diff --git a/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py b/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py index 813e6293f9..c5ba21626d 100644 --- a/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py +++ b/src/openai/types/chat/chat_completion_allowed_tool_choice_param.py @@ -10,6 +10,8 @@ class ChatCompletionAllowedToolChoiceParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + allowed_tools: Required[ChatCompletionAllowedToolsParam] """Constrains the tools available to the model to a pre-defined set.""" diff --git a/src/openai/types/chat/chat_completion_allowed_tools_param.py b/src/openai/types/chat/chat_completion_allowed_tools_param.py index d9b72d8f34..ac31fcb543 100644 --- a/src/openai/types/chat/chat_completion_allowed_tools_param.py +++ b/src/openai/types/chat/chat_completion_allowed_tools_param.py @@ -9,6 +9,8 @@ class ChatCompletionAllowedToolsParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + mode: Required[Literal["auto", "required"]] """Constrains the tools available to the model to a pre-defined set. diff --git a/src/openai/types/chat/chat_completion_assistant_message_param.py b/src/openai/types/chat/chat_completion_assistant_message_param.py index 1a08a959db..16a218438a 100644 --- a/src/openai/types/chat/chat_completion_assistant_message_param.py +++ b/src/openai/types/chat/chat_completion_assistant_message_param.py @@ -13,6 +13,11 @@ class Audio(TypedDict, total=False): + """ + Data about a previous audio response from the model. + [Learn more](https://platform.openai.com/docs/guides/audio). + """ + id: Required[str] """Unique identifier for a previous audio response from the model.""" @@ -21,6 +26,11 @@ class Audio(TypedDict, total=False): class FunctionCall(TypedDict, total=False): + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the model. + """ + arguments: Required[str] """ The arguments to call the function with, as generated by the model in JSON @@ -34,6 +44,8 @@ class FunctionCall(TypedDict, total=False): class ChatCompletionAssistantMessageParam(TypedDict, total=False): + """Messages sent by the model in response to user messages.""" + role: Required[Literal["assistant"]] """The role of the messages author, in this case `assistant`.""" diff --git a/src/openai/types/chat/chat_completion_audio.py b/src/openai/types/chat/chat_completion_audio.py index 232d60563d..df346d8c9d 100644 --- a/src/openai/types/chat/chat_completion_audio.py +++ b/src/openai/types/chat/chat_completion_audio.py @@ -6,6 +6,11 @@ class ChatCompletionAudio(BaseModel): + """ + If the audio output modality is requested, this object contains data + about the audio response from the model. [Learn more](https://platform.openai.com/docs/guides/audio). + """ + id: str """Unique identifier for this audio response.""" diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index b1576b41df..cac3c8b9d4 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -9,6 +9,12 @@ class ChatCompletionAudioParam(TypedDict, total=False): + """Parameters for audio output. + + Required when audio output is requested with + `modalities: ["audio"]`. [Learn more](https://platform.openai.com/docs/guides/audio). + """ + format: Required[Literal["wav", "aac", "mp3", "flac", "opus", "pcm16"]] """Specifies the output audio format. diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index ea32d157ef..ecbfd0a5aa 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -19,6 +19,11 @@ class ChoiceDeltaFunctionCall(BaseModel): + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the model. + """ + arguments: Optional[str] = None """ The arguments to call the function with, as generated by the model in JSON @@ -57,6 +62,8 @@ class ChoiceDeltaToolCall(BaseModel): class ChoiceDelta(BaseModel): + """A chat completion delta generated by streamed model responses.""" + content: Optional[str] = None """The contents of the chunk message.""" @@ -77,6 +84,8 @@ class ChoiceDelta(BaseModel): class ChoiceLogprobs(BaseModel): + """Log probability information for the choice.""" + content: Optional[List[ChatCompletionTokenLogprob]] = None """A list of message content tokens with log probability information.""" @@ -106,6 +115,12 @@ class Choice(BaseModel): class ChatCompletionChunk(BaseModel): + """ + Represents a streamed chunk of a chat completion response returned + by the model, based on the provided input. + [Learn more](https://platform.openai.com/docs/guides/streaming-responses). + """ + id: str """A unique identifier for the chat completion. Each chunk has the same ID.""" diff --git a/src/openai/types/chat/chat_completion_content_part_image.py b/src/openai/types/chat/chat_completion_content_part_image.py index c1386b9dd3..a636c51fb4 100644 --- a/src/openai/types/chat/chat_completion_content_part_image.py +++ b/src/openai/types/chat/chat_completion_content_part_image.py @@ -21,6 +21,8 @@ class ImageURL(BaseModel): class ChatCompletionContentPartImage(BaseModel): + """Learn about [image inputs](https://platform.openai.com/docs/guides/vision).""" + image_url: ImageURL type: Literal["image_url"] diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py index 9d407324d0..a230a340a7 100644 --- a/src/openai/types/chat/chat_completion_content_part_image_param.py +++ b/src/openai/types/chat/chat_completion_content_part_image_param.py @@ -20,6 +20,8 @@ class ImageURL(TypedDict, total=False): class ChatCompletionContentPartImageParam(TypedDict, total=False): + """Learn about [image inputs](https://platform.openai.com/docs/guides/vision).""" + image_url: Required[ImageURL] type: Required[Literal["image_url"]] diff --git a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py index 0b1b1a80b1..98d9e3c5eb 100644 --- a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py +++ b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py @@ -16,6 +16,8 @@ class InputAudio(TypedDict, total=False): class ChatCompletionContentPartInputAudioParam(TypedDict, total=False): + """Learn about [audio inputs](https://platform.openai.com/docs/guides/audio).""" + input_audio: Required[InputAudio] type: Required[Literal["input_audio"]] diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index cbedc853ba..b8c710a980 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -27,6 +27,10 @@ class FileFile(TypedDict, total=False): class File(TypedDict, total=False): + """ + Learn about [file inputs](https://platform.openai.com/docs/guides/text) for text generation. + """ + file: Required[FileFile] type: Required[Literal["file"]] diff --git a/src/openai/types/chat/chat_completion_content_part_text.py b/src/openai/types/chat/chat_completion_content_part_text.py index f09f35f708..e6d1bf1ec0 100644 --- a/src/openai/types/chat/chat_completion_content_part_text.py +++ b/src/openai/types/chat/chat_completion_content_part_text.py @@ -8,6 +8,10 @@ class ChatCompletionContentPartText(BaseModel): + """ + Learn about [text inputs](https://platform.openai.com/docs/guides/text-generation). + """ + text: str """The text content.""" diff --git a/src/openai/types/chat/chat_completion_content_part_text_param.py b/src/openai/types/chat/chat_completion_content_part_text_param.py index a270744417..be69bf66fa 100644 --- a/src/openai/types/chat/chat_completion_content_part_text_param.py +++ b/src/openai/types/chat/chat_completion_content_part_text_param.py @@ -8,6 +8,10 @@ class ChatCompletionContentPartTextParam(TypedDict, total=False): + """ + Learn about [text inputs](https://platform.openai.com/docs/guides/text-generation). + """ + text: Required[str] """The text content.""" diff --git a/src/openai/types/chat/chat_completion_custom_tool_param.py b/src/openai/types/chat/chat_completion_custom_tool_param.py index 14959ee449..d4f21ba0ca 100644 --- a/src/openai/types/chat/chat_completion_custom_tool_param.py +++ b/src/openai/types/chat/chat_completion_custom_tool_param.py @@ -16,11 +16,15 @@ class CustomFormatText(TypedDict, total=False): + """Unconstrained free-form text.""" + type: Required[Literal["text"]] """Unconstrained text format. Always `text`.""" class CustomFormatGrammarGrammar(TypedDict, total=False): + """Your chosen grammar.""" + definition: Required[str] """The grammar definition.""" @@ -29,6 +33,8 @@ class CustomFormatGrammarGrammar(TypedDict, total=False): class CustomFormatGrammar(TypedDict, total=False): + """A grammar defined by the user.""" + grammar: Required[CustomFormatGrammarGrammar] """Your chosen grammar.""" @@ -40,6 +46,8 @@ class CustomFormatGrammar(TypedDict, total=False): class Custom(TypedDict, total=False): + """Properties of the custom tool.""" + name: Required[str] """The name of the custom tool, used to identify it in tool calls.""" @@ -51,6 +59,8 @@ class Custom(TypedDict, total=False): class ChatCompletionCustomToolParam(TypedDict, total=False): + """A custom tool that processes input using a specified format.""" + custom: Required[Custom] """Properties of the custom tool.""" diff --git a/src/openai/types/chat/chat_completion_developer_message_param.py b/src/openai/types/chat/chat_completion_developer_message_param.py index 01e4fdb654..94fb3359f6 100644 --- a/src/openai/types/chat/chat_completion_developer_message_param.py +++ b/src/openai/types/chat/chat_completion_developer_message_param.py @@ -11,6 +11,12 @@ class ChatCompletionDeveloperMessageParam(TypedDict, total=False): + """ + Developer-provided instructions that the model should follow, regardless of + messages sent by the user. With o1 models and newer, `developer` messages + replace the previous `system` messages. + """ + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] """The contents of the developer message.""" diff --git a/src/openai/types/chat/chat_completion_function_call_option_param.py b/src/openai/types/chat/chat_completion_function_call_option_param.py index 2bc014af7a..b1ca37bf58 100644 --- a/src/openai/types/chat/chat_completion_function_call_option_param.py +++ b/src/openai/types/chat/chat_completion_function_call_option_param.py @@ -8,5 +8,9 @@ class ChatCompletionFunctionCallOptionParam(TypedDict, total=False): + """ + Specifying a particular function via `{"name": "my_function"}` forces the model to call that function. + """ + name: Required[str] """The name of the function to call.""" diff --git a/src/openai/types/chat/chat_completion_function_tool.py b/src/openai/types/chat/chat_completion_function_tool.py index 641568acf1..5d43a1e836 100644 --- a/src/openai/types/chat/chat_completion_function_tool.py +++ b/src/openai/types/chat/chat_completion_function_tool.py @@ -9,6 +9,8 @@ class ChatCompletionFunctionTool(BaseModel): + """A function tool that can be used to generate a response.""" + function: FunctionDefinition type: Literal["function"] diff --git a/src/openai/types/chat/chat_completion_function_tool_param.py b/src/openai/types/chat/chat_completion_function_tool_param.py index a39feea542..d336e8c08c 100644 --- a/src/openai/types/chat/chat_completion_function_tool_param.py +++ b/src/openai/types/chat/chat_completion_function_tool_param.py @@ -10,6 +10,8 @@ class ChatCompletionFunctionToolParam(TypedDict, total=False): + """A function tool that can be used to generate a response.""" + function: Required[FunctionDefinition] type: Required[Literal["function"]] diff --git a/src/openai/types/chat/chat_completion_message.py b/src/openai/types/chat/chat_completion_message.py index 5bb153fe3f..3f88f776b9 100644 --- a/src/openai/types/chat/chat_completion_message.py +++ b/src/openai/types/chat/chat_completion_message.py @@ -11,6 +11,8 @@ class AnnotationURLCitation(BaseModel): + """A URL citation when using web search.""" + end_index: int """The index of the last character of the URL citation in the message.""" @@ -25,6 +27,8 @@ class AnnotationURLCitation(BaseModel): class Annotation(BaseModel): + """A URL citation when using web search.""" + type: Literal["url_citation"] """The type of the URL citation. Always `url_citation`.""" @@ -33,6 +37,11 @@ class Annotation(BaseModel): class FunctionCall(BaseModel): + """Deprecated and replaced by `tool_calls`. + + The name and arguments of a function that should be called, as generated by the model. + """ + arguments: str """ The arguments to call the function with, as generated by the model in JSON @@ -46,6 +55,8 @@ class FunctionCall(BaseModel): class ChatCompletionMessage(BaseModel): + """A chat completion message generated by the model.""" + content: Optional[str] = None """The contents of the message.""" diff --git a/src/openai/types/chat/chat_completion_message_custom_tool_call.py b/src/openai/types/chat/chat_completion_message_custom_tool_call.py index b13c176afe..9542d8b924 100644 --- a/src/openai/types/chat/chat_completion_message_custom_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_custom_tool_call.py @@ -8,6 +8,8 @@ class Custom(BaseModel): + """The custom tool that the model called.""" + input: str """The input for the custom tool call generated by the model.""" @@ -16,6 +18,8 @@ class Custom(BaseModel): class ChatCompletionMessageCustomToolCall(BaseModel): + """A call to a custom tool created by the model.""" + id: str """The ID of the tool call.""" diff --git a/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py b/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py index 3753e0f200..3d03f0a93c 100644 --- a/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py +++ b/src/openai/types/chat/chat_completion_message_custom_tool_call_param.py @@ -8,6 +8,8 @@ class Custom(TypedDict, total=False): + """The custom tool that the model called.""" + input: Required[str] """The input for the custom tool call generated by the model.""" @@ -16,6 +18,8 @@ class Custom(TypedDict, total=False): class ChatCompletionMessageCustomToolCallParam(TypedDict, total=False): + """A call to a custom tool created by the model.""" + id: Required[str] """The ID of the tool call.""" diff --git a/src/openai/types/chat/chat_completion_message_function_tool_call.py b/src/openai/types/chat/chat_completion_message_function_tool_call.py index d056d9aff6..e7278b923c 100644 --- a/src/openai/types/chat/chat_completion_message_function_tool_call.py +++ b/src/openai/types/chat/chat_completion_message_function_tool_call.py @@ -8,6 +8,8 @@ class Function(BaseModel): + """The function that the model called.""" + arguments: str """ The arguments to call the function with, as generated by the model in JSON @@ -21,6 +23,8 @@ class Function(BaseModel): class ChatCompletionMessageFunctionToolCall(BaseModel): + """A call to a function tool created by the model.""" + id: str """The ID of the tool call.""" diff --git a/src/openai/types/chat/chat_completion_message_function_tool_call_param.py b/src/openai/types/chat/chat_completion_message_function_tool_call_param.py index 7c827edd2c..a8094ea63a 100644 --- a/src/openai/types/chat/chat_completion_message_function_tool_call_param.py +++ b/src/openai/types/chat/chat_completion_message_function_tool_call_param.py @@ -8,6 +8,8 @@ class Function(TypedDict, total=False): + """The function that the model called.""" + arguments: Required[str] """ The arguments to call the function with, as generated by the model in JSON @@ -21,6 +23,8 @@ class Function(TypedDict, total=False): class ChatCompletionMessageFunctionToolCallParam(TypedDict, total=False): + """A call to a function tool created by the model.""" + id: Required[str] """The ID of the tool call.""" diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py index 1c123c0acb..147fb87965 100644 --- a/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py +++ b/src/openai/types/chat/chat_completion_named_tool_choice_custom_param.py @@ -13,6 +13,11 @@ class Custom(TypedDict, total=False): class ChatCompletionNamedToolChoiceCustomParam(TypedDict, total=False): + """Specifies a tool the model should use. + + Use to force the model to call a specific custom tool. + """ + custom: Required[Custom] type: Required[Literal["custom"]] diff --git a/src/openai/types/chat/chat_completion_named_tool_choice_param.py b/src/openai/types/chat/chat_completion_named_tool_choice_param.py index ae1acfb909..f684fcea5e 100644 --- a/src/openai/types/chat/chat_completion_named_tool_choice_param.py +++ b/src/openai/types/chat/chat_completion_named_tool_choice_param.py @@ -13,6 +13,11 @@ class Function(TypedDict, total=False): class ChatCompletionNamedToolChoiceParam(TypedDict, total=False): + """Specifies a tool the model should use. + + Use to force the model to call a specific function. + """ + function: Required[Function] type: Required[Literal["function"]] diff --git a/src/openai/types/chat/chat_completion_prediction_content_param.py b/src/openai/types/chat/chat_completion_prediction_content_param.py index c44e6e3653..6184a314b5 100644 --- a/src/openai/types/chat/chat_completion_prediction_content_param.py +++ b/src/openai/types/chat/chat_completion_prediction_content_param.py @@ -11,6 +11,11 @@ class ChatCompletionPredictionContentParam(TypedDict, total=False): + """ + Static predicted output content, such as the content of a text file that is + being regenerated. + """ + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] """ The content that should be matched when generating a model response. If diff --git a/src/openai/types/chat/chat_completion_store_message.py b/src/openai/types/chat/chat_completion_store_message.py index 661342716b..6a805cce76 100644 --- a/src/openai/types/chat/chat_completion_store_message.py +++ b/src/openai/types/chat/chat_completion_store_message.py @@ -13,6 +13,8 @@ class ChatCompletionStoreMessage(ChatCompletionMessage): + """A chat completion message generated by the model.""" + id: str """The identifier of the chat message.""" diff --git a/src/openai/types/chat/chat_completion_stream_options_param.py b/src/openai/types/chat/chat_completion_stream_options_param.py index fc3191d2d1..9b881fff02 100644 --- a/src/openai/types/chat/chat_completion_stream_options_param.py +++ b/src/openai/types/chat/chat_completion_stream_options_param.py @@ -8,6 +8,8 @@ class ChatCompletionStreamOptionsParam(TypedDict, total=False): + """Options for streaming response. Only set this when you set `stream: true`.""" + include_obfuscation: bool """When true, stream obfuscation will be enabled. diff --git a/src/openai/types/chat/chat_completion_system_message_param.py b/src/openai/types/chat/chat_completion_system_message_param.py index 172ccea09e..9dcc5e07f9 100644 --- a/src/openai/types/chat/chat_completion_system_message_param.py +++ b/src/openai/types/chat/chat_completion_system_message_param.py @@ -11,6 +11,12 @@ class ChatCompletionSystemMessageParam(TypedDict, total=False): + """ + Developer-provided instructions that the model should follow, regardless of + messages sent by the user. With o1 models and newer, use `developer` messages + for this purpose instead. + """ + content: Required[Union[str, Iterable[ChatCompletionContentPartTextParam]]] """The contents of the system message.""" diff --git a/src/openai/types/chat/chat_completion_user_message_param.py b/src/openai/types/chat/chat_completion_user_message_param.py index 5c15322a22..c97ba535eb 100644 --- a/src/openai/types/chat/chat_completion_user_message_param.py +++ b/src/openai/types/chat/chat_completion_user_message_param.py @@ -11,6 +11,11 @@ class ChatCompletionUserMessageParam(TypedDict, total=False): + """ + Messages sent by an end user, containing prompts or additional context + information. + """ + content: Required[Union[str, Iterable[ChatCompletionContentPartParam]]] """The contents of the user message.""" diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index f2d55f7ec4..613787e9b5 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -382,6 +382,8 @@ class Function(TypedDict, total=False): class WebSearchOptionsUserLocationApproximate(TypedDict, total=False): + """Approximate location parameters for the search.""" + city: str """Free text input for the city of the user, e.g. `San Francisco`.""" @@ -402,6 +404,8 @@ class WebSearchOptionsUserLocationApproximate(TypedDict, total=False): class WebSearchOptionsUserLocation(TypedDict, total=False): + """Approximate location parameters for the search.""" + approximate: Required[WebSearchOptionsUserLocationApproximate] """Approximate location parameters for the search.""" @@ -410,6 +414,11 @@ class WebSearchOptionsUserLocation(TypedDict, total=False): class WebSearchOptions(TypedDict, total=False): + """ + This tool searches the web for relevant results to use in a response. + Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search?api-mode=chat). + """ + search_context_size: Literal["low", "medium", "high"] """ High level guidance for the amount of context window space to use for the diff --git a/src/openai/types/completion.py b/src/openai/types/completion.py index d3b3102a4a..ee59b2e209 100644 --- a/src/openai/types/completion.py +++ b/src/openai/types/completion.py @@ -11,6 +11,11 @@ class Completion(BaseModel): + """Represents a completion response from the API. + + Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint). + """ + id: str """A unique identifier for the completion.""" diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index d8c4e84cf7..9b5202da14 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -8,6 +8,8 @@ class CompletionTokensDetails(BaseModel): + """Breakdown of tokens used in a completion.""" + accepted_prediction_tokens: Optional[int] = None """ When using Predicted Outputs, the number of tokens in the prediction that @@ -30,6 +32,8 @@ class CompletionTokensDetails(BaseModel): class PromptTokensDetails(BaseModel): + """Breakdown of tokens used in the prompt.""" + audio_tokens: Optional[int] = None """Audio input tokens present in the prompt.""" @@ -38,6 +42,8 @@ class PromptTokensDetails(BaseModel): class CompletionUsage(BaseModel): + """Usage statistics for the completion request.""" + completion_tokens: int """Number of tokens in the generated completion.""" diff --git a/src/openai/types/container_create_params.py b/src/openai/types/container_create_params.py index d629c24d38..47101ecdb6 100644 --- a/src/openai/types/container_create_params.py +++ b/src/openai/types/container_create_params.py @@ -24,6 +24,8 @@ class ContainerCreateParams(TypedDict, total=False): class ExpiresAfter(TypedDict, total=False): + """Container expiration time in seconds relative to the 'anchor' time.""" + anchor: Required[Literal["last_active_at"]] """Time anchor for the expiration time. diff --git a/src/openai/types/container_create_response.py b/src/openai/types/container_create_response.py index cbad914283..0ebcc04062 100644 --- a/src/openai/types/container_create_response.py +++ b/src/openai/types/container_create_response.py @@ -9,6 +9,12 @@ class ExpiresAfter(BaseModel): + """ + The container will expire after this time period. + The anchor is the reference point for the expiration. + The minutes is the number of minutes after the anchor before the container expires. + """ + anchor: Optional[Literal["last_active_at"]] = None """The reference point for the expiration.""" diff --git a/src/openai/types/container_list_response.py b/src/openai/types/container_list_response.py index 29416f0941..8f39548201 100644 --- a/src/openai/types/container_list_response.py +++ b/src/openai/types/container_list_response.py @@ -9,6 +9,12 @@ class ExpiresAfter(BaseModel): + """ + The container will expire after this time period. + The anchor is the reference point for the expiration. + The minutes is the number of minutes after the anchor before the container expires. + """ + anchor: Optional[Literal["last_active_at"]] = None """The reference point for the expiration.""" diff --git a/src/openai/types/container_retrieve_response.py b/src/openai/types/container_retrieve_response.py index 31fedeac64..9ba3e18c3a 100644 --- a/src/openai/types/container_retrieve_response.py +++ b/src/openai/types/container_retrieve_response.py @@ -9,6 +9,12 @@ class ExpiresAfter(BaseModel): + """ + The container will expire after this time period. + The anchor is the reference point for the expiration. + The minutes is the number of minutes after the anchor before the container expires. + """ + anchor: Optional[Literal["last_active_at"]] = None """The reference point for the expiration.""" diff --git a/src/openai/types/conversations/computer_screenshot_content.py b/src/openai/types/conversations/computer_screenshot_content.py index 897b7ada0d..e42096eba2 100644 --- a/src/openai/types/conversations/computer_screenshot_content.py +++ b/src/openai/types/conversations/computer_screenshot_content.py @@ -9,6 +9,8 @@ class ComputerScreenshotContent(BaseModel): + """A screenshot of a computer.""" + file_id: Optional[str] = None """The identifier of an uploaded file that contains the screenshot.""" diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py index 052d09ce77..46268d381c 100644 --- a/src/openai/types/conversations/conversation_item.py +++ b/src/openai/types/conversations/conversation_item.py @@ -36,6 +36,8 @@ class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + id: str """The unique ID of the image generation call.""" @@ -50,6 +52,8 @@ class ImageGenerationCall(BaseModel): class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + command: List[str] """The command to run.""" @@ -70,6 +74,8 @@ class LocalShellCallAction(BaseModel): class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + id: str """The unique ID of the local shell call.""" @@ -87,6 +93,8 @@ class LocalShellCall(BaseModel): class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + id: str """The unique ID of the local shell tool call generated by the model.""" @@ -101,6 +109,8 @@ class LocalShellCallOutput(BaseModel): class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + input_schema: object """The JSON schema describing the tool's input.""" @@ -115,6 +125,8 @@ class McpListToolsTool(BaseModel): class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + id: str """The unique ID of the list.""" @@ -132,6 +144,8 @@ class McpListTools(BaseModel): class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + id: str """The unique ID of the approval request.""" @@ -149,6 +163,8 @@ class McpApprovalRequest(BaseModel): class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + id: str """The unique ID of the approval response""" @@ -166,6 +182,8 @@ class McpApprovalResponse(BaseModel): class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + id: str """The unique ID of the tool call.""" diff --git a/src/openai/types/conversations/conversation_item_list.py b/src/openai/types/conversations/conversation_item_list.py index 20091102cb..74d945d864 100644 --- a/src/openai/types/conversations/conversation_item_list.py +++ b/src/openai/types/conversations/conversation_item_list.py @@ -10,6 +10,8 @@ class ConversationItemList(BaseModel): + """A list of Conversation items.""" + data: List[ConversationItem] """A list of conversation items.""" diff --git a/src/openai/types/conversations/message.py b/src/openai/types/conversations/message.py index dbf5a14680..86c8860da8 100644 --- a/src/openai/types/conversations/message.py +++ b/src/openai/types/conversations/message.py @@ -18,6 +18,8 @@ class ContentReasoningText(BaseModel): + """Reasoning text from the model.""" + text: str """The reasoning text from the model.""" @@ -42,6 +44,8 @@ class ContentReasoningText(BaseModel): class Message(BaseModel): + """A message to or from the model.""" + id: str """The unique ID of the message.""" diff --git a/src/openai/types/conversations/summary_text_content.py b/src/openai/types/conversations/summary_text_content.py index d357b15725..6464a36599 100644 --- a/src/openai/types/conversations/summary_text_content.py +++ b/src/openai/types/conversations/summary_text_content.py @@ -8,6 +8,8 @@ class SummaryTextContent(BaseModel): + """A summary text from the model.""" + text: str """A summary of the reasoning output from the model so far.""" diff --git a/src/openai/types/conversations/text_content.py b/src/openai/types/conversations/text_content.py index f1ae079597..e602466c47 100644 --- a/src/openai/types/conversations/text_content.py +++ b/src/openai/types/conversations/text_content.py @@ -8,6 +8,8 @@ class TextContent(BaseModel): + """A text content.""" + text: str type: Literal["text"] diff --git a/src/openai/types/create_embedding_response.py b/src/openai/types/create_embedding_response.py index eff247a112..314a7f9afc 100644 --- a/src/openai/types/create_embedding_response.py +++ b/src/openai/types/create_embedding_response.py @@ -10,6 +10,8 @@ class Usage(BaseModel): + """The usage information for the request.""" + prompt_tokens: int """The number of tokens used by the prompt.""" diff --git a/src/openai/types/embedding.py b/src/openai/types/embedding.py index 769b1d165f..fbffec01e0 100644 --- a/src/openai/types/embedding.py +++ b/src/openai/types/embedding.py @@ -9,6 +9,8 @@ class Embedding(BaseModel): + """Represents an embedding vector returned by embedding endpoint.""" + embedding: List[float] """The embedding vector, which is a list of floats. diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index eb7f86cd92..0f2100b718 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -64,6 +64,13 @@ class EvalCreateParams(TypedDict, total=False): class DataSourceConfigCustom(TypedDict, total=False): + """ + A CustomDataSourceConfig object that defines the schema for the data source used for the evaluation runs. + This schema is used to define the shape of the data that will be: + - Used to define your testing criteria and + - What data is required when creating a run + """ + item_schema: Required[Dict[str, object]] """The json schema for each row in the data source.""" @@ -78,6 +85,11 @@ class DataSourceConfigCustom(TypedDict, total=False): class DataSourceConfigLogs(TypedDict, total=False): + """ + A data source config which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + """ + type: Required[Literal["logs"]] """The type of data source. Always `logs`.""" @@ -86,6 +98,8 @@ class DataSourceConfigLogs(TypedDict, total=False): class DataSourceConfigStoredCompletions(TypedDict, total=False): + """Deprecated in favor of LogsDataSourceConfig.""" + type: Required[Literal["stored_completions"]] """The type of data source. Always `stored_completions`.""" @@ -105,6 +119,8 @@ class TestingCriterionLabelModelInputSimpleInputMessage(TypedDict, total=False): class TestingCriterionLabelModelInputEvalItemContentOutputText(TypedDict, total=False): + """A text output from the model.""" + text: Required[str] """The text output from the model.""" @@ -113,6 +129,8 @@ class TestingCriterionLabelModelInputEvalItemContentOutputText(TypedDict, total= class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total=False): + """An image input to the model.""" + image_url: Required[str] """The URL of the image input.""" @@ -137,6 +155,14 @@ class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total= class TestingCriterionLabelModelInputEvalItem(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: Required[TestingCriterionLabelModelInputEvalItemContent] """Inputs to the model - can contain template strings.""" @@ -156,6 +182,11 @@ class TestingCriterionLabelModelInputEvalItem(TypedDict, total=False): class TestingCriterionLabelModel(TypedDict, total=False): + """ + A LabelModelGrader object which uses a model to assign labels to each item + in the evaluation. + """ + input: Required[Iterable[TestingCriterionLabelModelInput]] """A list of chat messages forming the prompt or context. @@ -179,16 +210,22 @@ class TestingCriterionLabelModel(TypedDict, total=False): class TestingCriterionTextSimilarity(TextSimilarityGraderParam, total=False): + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + pass_threshold: Required[float] """The threshold for the score.""" class TestingCriterionPython(PythonGraderParam, total=False): + """A PythonGrader object that runs a python script on the input.""" + pass_threshold: float """The threshold for the score.""" class TestingCriterionScoreModel(ScoreModelGraderParam, total=False): + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + pass_threshold: float """The threshold for the score.""" diff --git a/src/openai/types/eval_create_response.py b/src/openai/types/eval_create_response.py index 20b0e3127f..f3166422ba 100644 --- a/src/openai/types/eval_create_response.py +++ b/src/openai/types/eval_create_response.py @@ -28,6 +28,13 @@ class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + schema_: Dict[str, object] = FieldInfo(alias="schema") """ The json schema for the run data source items. Learn how to build JSON schemas @@ -56,18 +63,21 @@ class DataSourceConfigLogs(BaseModel): class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" pass_threshold: float """The threshold for the score.""" class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False + """A PythonGrader object that runs a python script on the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" @@ -82,6 +92,15 @@ class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): class EvalCreateResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-4o + """ + id: str """Unique identifier for the evaluation.""" diff --git a/src/openai/types/eval_custom_data_source_config.py b/src/openai/types/eval_custom_data_source_config.py index d99701cc71..6234c4f47a 100644 --- a/src/openai/types/eval_custom_data_source_config.py +++ b/src/openai/types/eval_custom_data_source_config.py @@ -11,6 +11,13 @@ class EvalCustomDataSourceConfig(BaseModel): + """ + A CustomDataSourceConfig which specifies the schema of your `item` and optionally `sample` namespaces. + The response schema defines the shape of the data that will be: + - Used to define your testing criteria and + - What data is required when creating a run + """ + schema_: Dict[str, object] = FieldInfo(alias="schema") """ The json schema for the run data source items. Learn how to build JSON schemas diff --git a/src/openai/types/eval_list_response.py b/src/openai/types/eval_list_response.py index 5ac4997cf6..7cd92c5a09 100644 --- a/src/openai/types/eval_list_response.py +++ b/src/openai/types/eval_list_response.py @@ -28,6 +28,13 @@ class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + schema_: Dict[str, object] = FieldInfo(alias="schema") """ The json schema for the run data source items. Learn how to build JSON schemas @@ -56,18 +63,21 @@ class DataSourceConfigLogs(BaseModel): class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" pass_threshold: float """The threshold for the score.""" class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False + """A PythonGrader object that runs a python script on the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" @@ -82,6 +92,15 @@ class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): class EvalListResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-4o + """ + id: str """Unique identifier for the evaluation.""" diff --git a/src/openai/types/eval_retrieve_response.py b/src/openai/types/eval_retrieve_response.py index 758f9cc040..56db7d6bc1 100644 --- a/src/openai/types/eval_retrieve_response.py +++ b/src/openai/types/eval_retrieve_response.py @@ -28,6 +28,13 @@ class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + schema_: Dict[str, object] = FieldInfo(alias="schema") """ The json schema for the run data source items. Learn how to build JSON schemas @@ -56,18 +63,21 @@ class DataSourceConfigLogs(BaseModel): class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" pass_threshold: float """The threshold for the score.""" class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False + """A PythonGrader object that runs a python script on the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" @@ -82,6 +92,15 @@ class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): class EvalRetrieveResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-4o + """ + id: str """Unique identifier for the evaluation.""" diff --git a/src/openai/types/eval_stored_completions_data_source_config.py b/src/openai/types/eval_stored_completions_data_source_config.py index 98f86a4719..d11f6ae14c 100644 --- a/src/openai/types/eval_stored_completions_data_source_config.py +++ b/src/openai/types/eval_stored_completions_data_source_config.py @@ -12,6 +12,8 @@ class EvalStoredCompletionsDataSourceConfig(BaseModel): + """Deprecated in favor of LogsDataSourceConfig.""" + schema_: Dict[str, object] = FieldInfo(alias="schema") """ The json schema for the run data source items. Learn how to build JSON schemas diff --git a/src/openai/types/eval_update_response.py b/src/openai/types/eval_update_response.py index 3f0b90ae03..30d4dbc3a1 100644 --- a/src/openai/types/eval_update_response.py +++ b/src/openai/types/eval_update_response.py @@ -28,6 +28,13 @@ class DataSourceConfigLogs(BaseModel): + """ + A LogsDataSourceConfig which specifies the metadata property of your logs query. + This is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc. + The schema returned by this data source config is used to defined what variables are available in your evals. + `item` and `sample` are both defined when using this data source config. + """ + schema_: Dict[str, object] = FieldInfo(alias="schema") """ The json schema for the run data source items. Learn how to build JSON schemas @@ -56,18 +63,21 @@ class DataSourceConfigLogs(BaseModel): class TestingCriterionEvalGraderTextSimilarity(TextSimilarityGrader): __test__ = False + """A TextSimilarityGrader object which grades text based on similarity metrics.""" pass_threshold: float """The threshold for the score.""" class TestingCriterionEvalGraderPython(PythonGrader): __test__ = False + """A PythonGrader object that runs a python script on the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): __test__ = False + """A ScoreModelGrader object that uses a model to assign a score to the input.""" pass_threshold: Optional[float] = None """The threshold for the score.""" @@ -82,6 +92,15 @@ class TestingCriterionEvalGraderScoreModel(ScoreModelGrader): class EvalUpdateResponse(BaseModel): + """ + An Eval object with a data source config and testing criteria. + An Eval represents a task to be done for your LLM integration. + Like: + - Improve the quality of my chatbot + - See how well my chatbot handles customer support + - Check if o4-mini is better at my usecase than gpt-4o + """ + id: str """Unique identifier for the evaluation.""" diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 4236746a17..6ec39873b7 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -58,6 +58,8 @@ class SourceFileID(BaseModel): class SourceStoredCompletions(BaseModel): + """A StoredCompletionsRunDataSource configuration describing a set of filters""" + type: Literal["stored_completions"] """The type of source. Always `stored_completions`.""" @@ -90,6 +92,8 @@ class SourceStoredCompletions(BaseModel): class InputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + text: str """The text output from the model.""" @@ -98,6 +102,8 @@ class InputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input to the model.""" + image_url: str """The URL of the image input.""" @@ -122,6 +128,14 @@ class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): class InputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: InputMessagesTemplateTemplateEvalItemContent """Inputs to the model - can contain template strings.""" @@ -217,6 +231,8 @@ class SamplingParams(BaseModel): class CreateEvalCompletionsRunDataSource(BaseModel): + """A CompletionsRunDataSource object describing a model sampling configuration.""" + source: Source """Determines what populates the `item` namespace in this run's data source.""" diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 751a1432b8..22d07c5598 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -58,6 +58,8 @@ class SourceFileID(TypedDict, total=False): class SourceStoredCompletions(TypedDict, total=False): + """A StoredCompletionsRunDataSource configuration describing a set of filters""" + type: Required[Literal["stored_completions"]] """The type of source. Always `stored_completions`.""" @@ -88,6 +90,8 @@ class SourceStoredCompletions(TypedDict, total=False): class InputMessagesTemplateTemplateEvalItemContentOutputText(TypedDict, total=False): + """A text output from the model.""" + text: Required[str] """The text output from the model.""" @@ -96,6 +100,8 @@ class InputMessagesTemplateTemplateEvalItemContentOutputText(TypedDict, total=Fa class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=False): + """An image input to the model.""" + image_url: Required[str] """The URL of the image input.""" @@ -120,6 +126,14 @@ class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=Fa class InputMessagesTemplateTemplateEvalItem(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: Required[InputMessagesTemplateTemplateEvalItemContent] """Inputs to the model - can contain template strings.""" @@ -213,6 +227,8 @@ class SamplingParams(TypedDict, total=False): class CreateEvalCompletionsRunDataSourceParam(TypedDict, total=False): + """A CompletionsRunDataSource object describing a model sampling configuration.""" + source: Required[Source] """Determines what populates the `item` namespace in this run's data source.""" diff --git a/src/openai/types/evals/create_eval_jsonl_run_data_source.py b/src/openai/types/evals/create_eval_jsonl_run_data_source.py index ae36f8c55f..36ede2d9eb 100644 --- a/src/openai/types/evals/create_eval_jsonl_run_data_source.py +++ b/src/openai/types/evals/create_eval_jsonl_run_data_source.py @@ -35,6 +35,10 @@ class SourceFileID(BaseModel): class CreateEvalJSONLRunDataSource(BaseModel): + """ + A JsonlRunDataSource object with that specifies a JSONL file that matches the eval + """ + source: Source """Determines what populates the `item` namespace in the data source.""" diff --git a/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py b/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py index 217ee36346..b87ba9c5df 100644 --- a/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_jsonl_run_data_source_param.py @@ -40,6 +40,10 @@ class SourceFileID(TypedDict, total=False): class CreateEvalJSONLRunDataSourceParam(TypedDict, total=False): + """ + A JsonlRunDataSource object with that specifies a JSONL file that matches the eval + """ + source: Required[Source] """Determines what populates the `item` namespace in the data source.""" diff --git a/src/openai/types/evals/eval_api_error.py b/src/openai/types/evals/eval_api_error.py index fe76871024..9b2c1871fb 100644 --- a/src/openai/types/evals/eval_api_error.py +++ b/src/openai/types/evals/eval_api_error.py @@ -6,6 +6,8 @@ class EvalAPIError(BaseModel): + """An object representing an error response from the Eval API.""" + code: str """The error code.""" diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index f7fb0ec4ad..40f071c959 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -66,6 +66,8 @@ class DataSourceResponsesSourceFileID(BaseModel): class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + type: Literal["responses"] """The type of run data source. Always `responses`.""" @@ -144,6 +146,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + text: str """The text output from the model.""" @@ -152,6 +156,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input to the model.""" + image_url: str """The URL of the image input.""" @@ -176,6 +182,14 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent """Inputs to the model - can contain template strings.""" @@ -221,6 +235,14 @@ class DataSourceResponsesInputMessagesItemReference(BaseModel): class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: Optional[ResponseFormatTextConfig] = None """An object specifying the format that the model must output. @@ -297,6 +319,8 @@ class DataSourceResponsesSamplingParams(BaseModel): class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + source: DataSourceResponsesSource """Determines what populates the `item` namespace in this run's data source.""" @@ -355,6 +379,8 @@ class PerTestingCriteriaResult(BaseModel): class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + errored: int """Number of output items that resulted in an error.""" @@ -369,6 +395,8 @@ class ResultCounts(BaseModel): class RunCancelResponse(BaseModel): + """A schema representing an evaluation run.""" + id: str """Unique identifier for the evaluation run.""" diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index a70d1923e5..993e10c738 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -79,6 +79,8 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceFileID(TypedDict, total=Fa class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total=False): + """A EvalResponsesSource object describing a run data source configuration.""" + type: Required[Literal["responses"]] """The type of run data source. Always `responses`.""" @@ -160,6 +162,8 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateCha class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText( TypedDict, total=False ): + """A text output from the model.""" + text: Required[str] """The text output from the model.""" @@ -170,6 +174,8 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage( TypedDict, total=False ): + """An image input to the model.""" + image_url: Required[str] """The URL of the image input.""" @@ -194,6 +200,14 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: Required[DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent] """Inputs to the model - can contain template strings.""" @@ -239,6 +253,14 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference(Typed class DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: ResponseFormatTextConfigParam """An object specifying the format that the model must output. @@ -315,6 +337,8 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= class DataSourceCreateEvalResponsesRunDataSource(TypedDict, total=False): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + source: Required[DataSourceCreateEvalResponsesRunDataSourceSource] """Determines what populates the `item` namespace in this run's data source.""" diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index fb2220b3a1..93dae7adde 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -66,6 +66,8 @@ class DataSourceResponsesSourceFileID(BaseModel): class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + type: Literal["responses"] """The type of run data source. Always `responses`.""" @@ -144,6 +146,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + text: str """The text output from the model.""" @@ -152,6 +156,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input to the model.""" + image_url: str """The URL of the image input.""" @@ -176,6 +182,14 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent """Inputs to the model - can contain template strings.""" @@ -221,6 +235,14 @@ class DataSourceResponsesInputMessagesItemReference(BaseModel): class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: Optional[ResponseFormatTextConfig] = None """An object specifying the format that the model must output. @@ -297,6 +319,8 @@ class DataSourceResponsesSamplingParams(BaseModel): class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + source: DataSourceResponsesSource """Determines what populates the `item` namespace in this run's data source.""" @@ -355,6 +379,8 @@ class PerTestingCriteriaResult(BaseModel): class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + errored: int """Number of output items that resulted in an error.""" @@ -369,6 +395,8 @@ class ResultCounts(BaseModel): class RunCreateResponse(BaseModel): + """A schema representing an evaluation run.""" + id: str """Unique identifier for the evaluation run.""" diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index adac4ffdc8..c3c745b21c 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -66,6 +66,8 @@ class DataSourceResponsesSourceFileID(BaseModel): class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + type: Literal["responses"] """The type of run data source. Always `responses`.""" @@ -144,6 +146,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + text: str """The text output from the model.""" @@ -152,6 +156,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input to the model.""" + image_url: str """The URL of the image input.""" @@ -176,6 +182,14 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent """Inputs to the model - can contain template strings.""" @@ -221,6 +235,14 @@ class DataSourceResponsesInputMessagesItemReference(BaseModel): class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: Optional[ResponseFormatTextConfig] = None """An object specifying the format that the model must output. @@ -297,6 +319,8 @@ class DataSourceResponsesSamplingParams(BaseModel): class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + source: DataSourceResponsesSource """Determines what populates the `item` namespace in this run's data source.""" @@ -355,6 +379,8 @@ class PerTestingCriteriaResult(BaseModel): class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + errored: int """Number of output items that resulted in an error.""" @@ -369,6 +395,8 @@ class ResultCounts(BaseModel): class RunListResponse(BaseModel): + """A schema representing an evaluation run.""" + id: str """Unique identifier for the evaluation run.""" diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index abdc5ebae5..d02256c679 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -66,6 +66,8 @@ class DataSourceResponsesSourceFileID(BaseModel): class DataSourceResponsesSourceResponses(BaseModel): + """A EvalResponsesSource object describing a run data source configuration.""" + type: Literal["responses"] """The type of run data source. Always `responses`.""" @@ -144,6 +146,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateChatMessage(BaseModel): class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): + """A text output from the model.""" + text: str """The text output from the model.""" @@ -152,6 +156,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): + """An image input to the model.""" + image_url: str """The URL of the image input.""" @@ -176,6 +182,14 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent """Inputs to the model - can contain template strings.""" @@ -221,6 +235,14 @@ class DataSourceResponsesInputMessagesItemReference(BaseModel): class DataSourceResponsesSamplingParamsText(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: Optional[ResponseFormatTextConfig] = None """An object specifying the format that the model must output. @@ -297,6 +319,8 @@ class DataSourceResponsesSamplingParams(BaseModel): class DataSourceResponses(BaseModel): + """A ResponsesRunDataSource object describing a model sampling configuration.""" + source: DataSourceResponsesSource """Determines what populates the `item` namespace in this run's data source.""" @@ -355,6 +379,8 @@ class PerTestingCriteriaResult(BaseModel): class ResultCounts(BaseModel): + """Counters summarizing the outcomes of the evaluation run.""" + errored: int """Number of output items that resulted in an error.""" @@ -369,6 +395,8 @@ class ResultCounts(BaseModel): class RunRetrieveResponse(BaseModel): + """A schema representing an evaluation run.""" + id: str """Unique identifier for the evaluation run.""" diff --git a/src/openai/types/evals/runs/output_item_list_response.py b/src/openai/types/evals/runs/output_item_list_response.py index e88c21766f..a906a29df7 100644 --- a/src/openai/types/evals/runs/output_item_list_response.py +++ b/src/openai/types/evals/runs/output_item_list_response.py @@ -12,6 +12,8 @@ class Result(BaseModel): + """A single grader result for an evaluation run output item.""" + name: str """The name of the grader.""" @@ -41,6 +43,8 @@ def __getattr__(self, attr: str) -> object: ... class SampleInput(BaseModel): + """An input message.""" + content: str """The content of the message.""" @@ -57,6 +61,8 @@ class SampleOutput(BaseModel): class SampleUsage(BaseModel): + """Token usage details for the sample.""" + cached_tokens: int """The number of tokens retrieved from cache.""" @@ -71,6 +77,8 @@ class SampleUsage(BaseModel): class Sample(BaseModel): + """A sample containing the input and output of the evaluation run.""" + error: EvalAPIError """An object representing an error response from the Eval API.""" @@ -103,6 +111,8 @@ class Sample(BaseModel): class OutputItemListResponse(BaseModel): + """A schema representing an evaluation run output item.""" + id: str """Unique identifier for the evaluation run output item.""" diff --git a/src/openai/types/evals/runs/output_item_retrieve_response.py b/src/openai/types/evals/runs/output_item_retrieve_response.py index c728629b41..42ba4b2864 100644 --- a/src/openai/types/evals/runs/output_item_retrieve_response.py +++ b/src/openai/types/evals/runs/output_item_retrieve_response.py @@ -12,6 +12,8 @@ class Result(BaseModel): + """A single grader result for an evaluation run output item.""" + name: str """The name of the grader.""" @@ -41,6 +43,8 @@ def __getattr__(self, attr: str) -> object: ... class SampleInput(BaseModel): + """An input message.""" + content: str """The content of the message.""" @@ -57,6 +61,8 @@ class SampleOutput(BaseModel): class SampleUsage(BaseModel): + """Token usage details for the sample.""" + cached_tokens: int """The number of tokens retrieved from cache.""" @@ -71,6 +77,8 @@ class SampleUsage(BaseModel): class Sample(BaseModel): + """A sample containing the input and output of the evaluation run.""" + error: EvalAPIError """An object representing an error response from the Eval API.""" @@ -103,6 +111,8 @@ class Sample(BaseModel): class OutputItemRetrieveResponse(BaseModel): + """A schema representing an evaluation run output item.""" + id: str """Unique identifier for the evaluation run output item.""" diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index f4583b16a3..f4367f7a7d 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -32,6 +32,11 @@ class FileCreateParams(TypedDict, total=False): class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted. + """ + anchor: Required[Literal["created_at"]] """Anchor timestamp after which the expiration policy applies. diff --git a/src/openai/types/file_object.py b/src/openai/types/file_object.py index 883c2de019..4a9901fd3f 100644 --- a/src/openai/types/file_object.py +++ b/src/openai/types/file_object.py @@ -9,6 +9,8 @@ class FileObject(BaseModel): + """The `File` object represents a document that has been uploaded to OpenAI.""" + id: str """The file identifier, which can be referenced in the API endpoints.""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_create_response.py b/src/openai/types/fine_tuning/checkpoints/permission_create_response.py index 9bc14c00cc..459fa9dee7 100644 --- a/src/openai/types/fine_tuning/checkpoints/permission_create_response.py +++ b/src/openai/types/fine_tuning/checkpoints/permission_create_response.py @@ -8,6 +8,10 @@ class PermissionCreateResponse(BaseModel): + """ + The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint. + """ + id: str """The permission identifier, which can be referenced in the API endpoints.""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py index 14c73b55d0..34208958ef 100644 --- a/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py +++ b/src/openai/types/fine_tuning/checkpoints/permission_retrieve_response.py @@ -9,6 +9,10 @@ class Data(BaseModel): + """ + The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint. + """ + id: str """The permission identifier, which can be referenced in the API endpoints.""" diff --git a/src/openai/types/fine_tuning/dpo_hyperparameters.py b/src/openai/types/fine_tuning/dpo_hyperparameters.py index b0b3f0581b..cd39f308a4 100644 --- a/src/openai/types/fine_tuning/dpo_hyperparameters.py +++ b/src/openai/types/fine_tuning/dpo_hyperparameters.py @@ -9,6 +9,8 @@ class DpoHyperparameters(BaseModel): + """The hyperparameters used for the DPO fine-tuning job.""" + batch_size: Union[Literal["auto"], int, None] = None """Number of examples in each batch. diff --git a/src/openai/types/fine_tuning/dpo_hyperparameters_param.py b/src/openai/types/fine_tuning/dpo_hyperparameters_param.py index 87c6ee80a5..12b2c41ca8 100644 --- a/src/openai/types/fine_tuning/dpo_hyperparameters_param.py +++ b/src/openai/types/fine_tuning/dpo_hyperparameters_param.py @@ -9,6 +9,8 @@ class DpoHyperparametersParam(TypedDict, total=False): + """The hyperparameters used for the DPO fine-tuning job.""" + batch_size: Union[Literal["auto"], int] """Number of examples in each batch. diff --git a/src/openai/types/fine_tuning/dpo_method.py b/src/openai/types/fine_tuning/dpo_method.py index 3e20f360dd..452c182016 100644 --- a/src/openai/types/fine_tuning/dpo_method.py +++ b/src/openai/types/fine_tuning/dpo_method.py @@ -9,5 +9,7 @@ class DpoMethod(BaseModel): + """Configuration for the DPO fine-tuning method.""" + hyperparameters: Optional[DpoHyperparameters] = None """The hyperparameters used for the DPO fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/dpo_method_param.py b/src/openai/types/fine_tuning/dpo_method_param.py index ce6b6510f6..6bd74d9760 100644 --- a/src/openai/types/fine_tuning/dpo_method_param.py +++ b/src/openai/types/fine_tuning/dpo_method_param.py @@ -10,5 +10,7 @@ class DpoMethodParam(TypedDict, total=False): + """Configuration for the DPO fine-tuning method.""" + hyperparameters: DpoHyperparametersParam """The hyperparameters used for the DPO fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/fine_tuning_job.py b/src/openai/types/fine_tuning/fine_tuning_job.py index f626fbba64..bb8a4d597b 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job.py +++ b/src/openai/types/fine_tuning/fine_tuning_job.py @@ -14,6 +14,10 @@ class Error(BaseModel): + """ + For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure. + """ + code: str """A machine-readable error code.""" @@ -28,6 +32,11 @@ class Error(BaseModel): class Hyperparameters(BaseModel): + """The hyperparameters used for the fine-tuning job. + + This value will only be returned when running `supervised` jobs. + """ + batch_size: Union[Literal["auto"], int, None] = None """Number of examples in each batch. @@ -49,6 +58,8 @@ class Hyperparameters(BaseModel): class Method(BaseModel): + """The method used for fine-tuning.""" + type: Literal["supervised", "dpo", "reinforcement"] """The type of method. Is either `supervised`, `dpo`, or `reinforcement`.""" @@ -63,6 +74,10 @@ class Method(BaseModel): class FineTuningJob(BaseModel): + """ + The `fine_tuning.job` object represents a fine-tuning job that has been created through the API. + """ + id: str """The object identifier, which can be referenced in the API endpoints.""" diff --git a/src/openai/types/fine_tuning/fine_tuning_job_event.py b/src/openai/types/fine_tuning/fine_tuning_job_event.py index 1d728bd765..7452b818c6 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_event.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_event.py @@ -10,6 +10,8 @@ class FineTuningJobEvent(BaseModel): + """Fine-tuning job event object""" + id: str """The object identifier.""" diff --git a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py index 4ac282eb54..0e33aa84c8 100644 --- a/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py +++ b/src/openai/types/fine_tuning/fine_tuning_job_wandb_integration.py @@ -8,6 +8,13 @@ class FineTuningJobWandbIntegration(BaseModel): + """The settings for your integration with Weights and Biases. + + This payload specifies the project that + metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags + to your run, and set a default entity (team, username, etc) to be associated with your run. + """ + project: str """The name of the project that the new run will be created under.""" diff --git a/src/openai/types/fine_tuning/job_create_params.py b/src/openai/types/fine_tuning/job_create_params.py index 351d4e0e1b..181bede2d9 100644 --- a/src/openai/types/fine_tuning/job_create_params.py +++ b/src/openai/types/fine_tuning/job_create_params.py @@ -100,6 +100,11 @@ class JobCreateParams(TypedDict, total=False): class Hyperparameters(TypedDict, total=False): + """ + The hyperparameters used for the fine-tuning job. + This value is now deprecated in favor of `method`, and should be passed in under the `method` parameter. + """ + batch_size: Union[Literal["auto"], int] """Number of examples in each batch. @@ -121,6 +126,13 @@ class Hyperparameters(TypedDict, total=False): class IntegrationWandb(TypedDict, total=False): + """The settings for your integration with Weights and Biases. + + This payload specifies the project that + metrics will be sent to. Optionally, you can set an explicit display name for your run, add tags + to your run, and set a default entity (team, username, etc) to be associated with your run. + """ + project: Required[str] """The name of the project that the new run will be created under.""" @@ -163,6 +175,8 @@ class Integration(TypedDict, total=False): class Method(TypedDict, total=False): + """The method used for fine-tuning.""" + type: Required[Literal["supervised", "dpo", "reinforcement"]] """The type of method. Is either `supervised`, `dpo`, or `reinforcement`.""" diff --git a/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py b/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py index bd07317a3e..f8a04b6395 100644 --- a/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py +++ b/src/openai/types/fine_tuning/jobs/fine_tuning_job_checkpoint.py @@ -9,6 +9,8 @@ class Metrics(BaseModel): + """Metrics at the step number during the fine-tuning job.""" + full_valid_loss: Optional[float] = None full_valid_mean_token_accuracy: Optional[float] = None @@ -25,6 +27,10 @@ class Metrics(BaseModel): class FineTuningJobCheckpoint(BaseModel): + """ + The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that is ready to use. + """ + id: str """The checkpoint identifier, which can be referenced in the API endpoints.""" diff --git a/src/openai/types/fine_tuning/reinforcement_hyperparameters.py b/src/openai/types/fine_tuning/reinforcement_hyperparameters.py index 7c1762d38c..4c289fd659 100644 --- a/src/openai/types/fine_tuning/reinforcement_hyperparameters.py +++ b/src/openai/types/fine_tuning/reinforcement_hyperparameters.py @@ -9,6 +9,8 @@ class ReinforcementHyperparameters(BaseModel): + """The hyperparameters used for the reinforcement fine-tuning job.""" + batch_size: Union[Literal["auto"], int, None] = None """Number of examples in each batch. diff --git a/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py b/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py index 0cc12fcb17..7be716f143 100644 --- a/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py +++ b/src/openai/types/fine_tuning/reinforcement_hyperparameters_param.py @@ -9,6 +9,8 @@ class ReinforcementHyperparametersParam(TypedDict, total=False): + """The hyperparameters used for the reinforcement fine-tuning job.""" + batch_size: Union[Literal["auto"], int] """Number of examples in each batch. diff --git a/src/openai/types/fine_tuning/reinforcement_method.py b/src/openai/types/fine_tuning/reinforcement_method.py index 9b65c41033..a8a3685148 100644 --- a/src/openai/types/fine_tuning/reinforcement_method.py +++ b/src/openai/types/fine_tuning/reinforcement_method.py @@ -17,6 +17,8 @@ class ReinforcementMethod(BaseModel): + """Configuration for the reinforcement fine-tuning method.""" + grader: Grader """The grader used for the fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/reinforcement_method_param.py b/src/openai/types/fine_tuning/reinforcement_method_param.py index 00d5060536..ea75bfeb69 100644 --- a/src/openai/types/fine_tuning/reinforcement_method_param.py +++ b/src/openai/types/fine_tuning/reinforcement_method_param.py @@ -20,6 +20,8 @@ class ReinforcementMethodParam(TypedDict, total=False): + """Configuration for the reinforcement fine-tuning method.""" + grader: Required[Grader] """The grader used for the fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/supervised_hyperparameters.py b/src/openai/types/fine_tuning/supervised_hyperparameters.py index 3955ecf437..1231bbdd80 100644 --- a/src/openai/types/fine_tuning/supervised_hyperparameters.py +++ b/src/openai/types/fine_tuning/supervised_hyperparameters.py @@ -9,6 +9,8 @@ class SupervisedHyperparameters(BaseModel): + """The hyperparameters used for the fine-tuning job.""" + batch_size: Union[Literal["auto"], int, None] = None """Number of examples in each batch. diff --git a/src/openai/types/fine_tuning/supervised_hyperparameters_param.py b/src/openai/types/fine_tuning/supervised_hyperparameters_param.py index bd37d9b239..de0e021dea 100644 --- a/src/openai/types/fine_tuning/supervised_hyperparameters_param.py +++ b/src/openai/types/fine_tuning/supervised_hyperparameters_param.py @@ -9,6 +9,8 @@ class SupervisedHyperparametersParam(TypedDict, total=False): + """The hyperparameters used for the fine-tuning job.""" + batch_size: Union[Literal["auto"], int] """Number of examples in each batch. diff --git a/src/openai/types/fine_tuning/supervised_method.py b/src/openai/types/fine_tuning/supervised_method.py index 3a32bf27a0..96e102582d 100644 --- a/src/openai/types/fine_tuning/supervised_method.py +++ b/src/openai/types/fine_tuning/supervised_method.py @@ -9,5 +9,7 @@ class SupervisedMethod(BaseModel): + """Configuration for the supervised fine-tuning method.""" + hyperparameters: Optional[SupervisedHyperparameters] = None """The hyperparameters used for the fine-tuning job.""" diff --git a/src/openai/types/fine_tuning/supervised_method_param.py b/src/openai/types/fine_tuning/supervised_method_param.py index ba277853d7..4381cd184b 100644 --- a/src/openai/types/fine_tuning/supervised_method_param.py +++ b/src/openai/types/fine_tuning/supervised_method_param.py @@ -10,5 +10,7 @@ class SupervisedMethodParam(TypedDict, total=False): + """Configuration for the supervised fine-tuning method.""" + hyperparameters: SupervisedHyperparametersParam """The hyperparameters used for the fine-tuning job.""" diff --git a/src/openai/types/graders/label_model_grader.py b/src/openai/types/graders/label_model_grader.py index 0929349c24..141306b510 100644 --- a/src/openai/types/graders/label_model_grader.py +++ b/src/openai/types/graders/label_model_grader.py @@ -11,6 +11,8 @@ class InputContentOutputText(BaseModel): + """A text output from the model.""" + text: str """The text output from the model.""" @@ -19,6 +21,8 @@ class InputContentOutputText(BaseModel): class InputContentInputImage(BaseModel): + """An image input to the model.""" + image_url: str """The URL of the image input.""" @@ -38,6 +42,14 @@ class InputContentInputImage(BaseModel): class Input(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: InputContent """Inputs to the model - can contain template strings.""" @@ -52,6 +64,11 @@ class Input(BaseModel): class LabelModelGrader(BaseModel): + """ + A LabelModelGrader object which uses a model to assign labels to each item + in the evaluation. + """ + input: List[Input] labels: List[str] diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py index 7bd6fdb4a7..a23be2a236 100644 --- a/src/openai/types/graders/label_model_grader_param.py +++ b/src/openai/types/graders/label_model_grader_param.py @@ -13,6 +13,8 @@ class InputContentOutputText(TypedDict, total=False): + """A text output from the model.""" + text: Required[str] """The text output from the model.""" @@ -21,6 +23,8 @@ class InputContentOutputText(TypedDict, total=False): class InputContentInputImage(TypedDict, total=False): + """An image input to the model.""" + image_url: Required[str] """The URL of the image input.""" @@ -45,6 +49,14 @@ class InputContentInputImage(TypedDict, total=False): class Input(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: Required[InputContent] """Inputs to the model - can contain template strings.""" @@ -59,6 +71,11 @@ class Input(TypedDict, total=False): class LabelModelGraderParam(TypedDict, total=False): + """ + A LabelModelGrader object which uses a model to assign labels to each item + in the evaluation. + """ + input: Required[Iterable[Input]] labels: Required[SequenceNotStr[str]] diff --git a/src/openai/types/graders/multi_grader.py b/src/openai/types/graders/multi_grader.py index 7539c68ef5..022ddb406a 100644 --- a/src/openai/types/graders/multi_grader.py +++ b/src/openai/types/graders/multi_grader.py @@ -16,6 +16,10 @@ class MultiGrader(BaseModel): + """ + A MultiGrader object combines the output of multiple graders to produce a single score. + """ + calculate_output: str """A formula to calculate the output based on grader results.""" diff --git a/src/openai/types/graders/multi_grader_param.py b/src/openai/types/graders/multi_grader_param.py index 28a6705b81..064267a5aa 100644 --- a/src/openai/types/graders/multi_grader_param.py +++ b/src/openai/types/graders/multi_grader_param.py @@ -19,6 +19,10 @@ class MultiGraderParam(TypedDict, total=False): + """ + A MultiGrader object combines the output of multiple graders to produce a single score. + """ + calculate_output: Required[str] """A formula to calculate the output based on grader results.""" diff --git a/src/openai/types/graders/python_grader.py b/src/openai/types/graders/python_grader.py index faa10b1ef9..81aafdae0a 100644 --- a/src/openai/types/graders/python_grader.py +++ b/src/openai/types/graders/python_grader.py @@ -9,6 +9,8 @@ class PythonGrader(BaseModel): + """A PythonGrader object that runs a python script on the input.""" + name: str """The name of the grader.""" diff --git a/src/openai/types/graders/python_grader_param.py b/src/openai/types/graders/python_grader_param.py index efb923751e..3be7bab432 100644 --- a/src/openai/types/graders/python_grader_param.py +++ b/src/openai/types/graders/python_grader_param.py @@ -8,6 +8,8 @@ class PythonGraderParam(TypedDict, total=False): + """A PythonGrader object that runs a python script on the input.""" + name: Required[str] """The name of the grader.""" diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index b3ba6758bb..6dd5a0eee8 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -19,6 +19,8 @@ class InputContentOutputText(BaseModel): + """A text output from the model.""" + text: str """The text output from the model.""" @@ -27,6 +29,8 @@ class InputContentOutputText(BaseModel): class InputContentInputImage(BaseModel): + """An image input to the model.""" + image_url: str """The URL of the image input.""" @@ -46,6 +50,14 @@ class InputContentInputImage(BaseModel): class Input(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: InputContent """Inputs to the model - can contain template strings.""" @@ -60,6 +72,8 @@ class Input(BaseModel): class SamplingParams(BaseModel): + """The sampling parameters for the model.""" + max_completions_tokens: Optional[int] = None """The maximum number of tokens the grader model may generate in its response.""" @@ -91,6 +105,8 @@ class SamplingParams(BaseModel): class ScoreModelGrader(BaseModel): + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + input: List[Input] """The input text. This may include template strings.""" diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index eb1f6e03ac..33452e43f3 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -20,6 +20,8 @@ class InputContentOutputText(TypedDict, total=False): + """A text output from the model.""" + text: Required[str] """The text output from the model.""" @@ -28,6 +30,8 @@ class InputContentOutputText(TypedDict, total=False): class InputContentInputImage(TypedDict, total=False): + """An image input to the model.""" + image_url: Required[str] """The URL of the image input.""" @@ -52,6 +56,14 @@ class InputContentInputImage(TypedDict, total=False): class Input(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: Required[InputContent] """Inputs to the model - can contain template strings.""" @@ -66,6 +78,8 @@ class Input(TypedDict, total=False): class SamplingParams(TypedDict, total=False): + """The sampling parameters for the model.""" + max_completions_tokens: Optional[int] """The maximum number of tokens the grader model may generate in its response.""" @@ -97,6 +111,8 @@ class SamplingParams(TypedDict, total=False): class ScoreModelGraderParam(TypedDict, total=False): + """A ScoreModelGrader object that uses a model to assign a score to the input.""" + input: Required[Iterable[Input]] """The input text. This may include template strings.""" diff --git a/src/openai/types/graders/string_check_grader.py b/src/openai/types/graders/string_check_grader.py index 3bf0b8c868..efd3679da9 100644 --- a/src/openai/types/graders/string_check_grader.py +++ b/src/openai/types/graders/string_check_grader.py @@ -8,6 +8,10 @@ class StringCheckGrader(BaseModel): + """ + A StringCheckGrader object that performs a string comparison between input and reference using a specified operation. + """ + input: str """The input text. This may include template strings.""" diff --git a/src/openai/types/graders/string_check_grader_param.py b/src/openai/types/graders/string_check_grader_param.py index 27b204cec0..da9e961568 100644 --- a/src/openai/types/graders/string_check_grader_param.py +++ b/src/openai/types/graders/string_check_grader_param.py @@ -8,6 +8,10 @@ class StringCheckGraderParam(TypedDict, total=False): + """ + A StringCheckGrader object that performs a string comparison between input and reference using a specified operation. + """ + input: Required[str] """The input text. This may include template strings.""" diff --git a/src/openai/types/graders/text_similarity_grader.py b/src/openai/types/graders/text_similarity_grader.py index 9082ac8969..a9d39a2fbd 100644 --- a/src/openai/types/graders/text_similarity_grader.py +++ b/src/openai/types/graders/text_similarity_grader.py @@ -8,6 +8,8 @@ class TextSimilarityGrader(BaseModel): + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + evaluation_metric: Literal[ "cosine", "fuzzy_match", diff --git a/src/openai/types/graders/text_similarity_grader_param.py b/src/openai/types/graders/text_similarity_grader_param.py index 1646afc84b..0907c3c2a7 100644 --- a/src/openai/types/graders/text_similarity_grader_param.py +++ b/src/openai/types/graders/text_similarity_grader_param.py @@ -8,6 +8,8 @@ class TextSimilarityGraderParam(TypedDict, total=False): + """A TextSimilarityGrader object which grades text based on similarity metrics.""" + evaluation_metric: Required[ Literal[ "cosine", diff --git a/src/openai/types/image.py b/src/openai/types/image.py index ecaef3fd58..9e2a23fa40 100644 --- a/src/openai/types/image.py +++ b/src/openai/types/image.py @@ -8,6 +8,8 @@ class Image(BaseModel): + """Represents the content or the URL of an image generated by the OpenAI API.""" + b64_json: Optional[str] = None """The base64-encoded JSON of the generated image. diff --git a/src/openai/types/image_edit_completed_event.py b/src/openai/types/image_edit_completed_event.py index a40682da6a..5bd2986d2a 100644 --- a/src/openai/types/image_edit_completed_event.py +++ b/src/openai/types/image_edit_completed_event.py @@ -8,6 +8,8 @@ class UsageInputTokensDetails(BaseModel): + """The input tokens detailed information for the image generation.""" + image_tokens: int """The number of image tokens in the input prompt.""" @@ -16,6 +18,8 @@ class UsageInputTokensDetails(BaseModel): class Usage(BaseModel): + """For `gpt-image-1` only, the token usage information for the image generation.""" + input_tokens: int """The number of tokens (images and text) in the input prompt.""" @@ -30,6 +34,8 @@ class Usage(BaseModel): class ImageEditCompletedEvent(BaseModel): + """Emitted when image editing has completed and the final image is available.""" + b64_json: str """Base64-encoded final edited image data, suitable for rendering as an image.""" diff --git a/src/openai/types/image_edit_partial_image_event.py b/src/openai/types/image_edit_partial_image_event.py index 20da45efc3..7bbd8c9b13 100644 --- a/src/openai/types/image_edit_partial_image_event.py +++ b/src/openai/types/image_edit_partial_image_event.py @@ -8,6 +8,8 @@ class ImageEditPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image editing streaming.""" + b64_json: str """Base64-encoded partial image data, suitable for rendering as an image.""" diff --git a/src/openai/types/image_gen_completed_event.py b/src/openai/types/image_gen_completed_event.py index e78da842d4..dc9ccb8cfc 100644 --- a/src/openai/types/image_gen_completed_event.py +++ b/src/openai/types/image_gen_completed_event.py @@ -8,6 +8,8 @@ class UsageInputTokensDetails(BaseModel): + """The input tokens detailed information for the image generation.""" + image_tokens: int """The number of image tokens in the input prompt.""" @@ -16,6 +18,8 @@ class UsageInputTokensDetails(BaseModel): class Usage(BaseModel): + """For `gpt-image-1` only, the token usage information for the image generation.""" + input_tokens: int """The number of tokens (images and text) in the input prompt.""" @@ -30,6 +34,8 @@ class Usage(BaseModel): class ImageGenCompletedEvent(BaseModel): + """Emitted when image generation has completed and the final image is available.""" + b64_json: str """Base64-encoded image data, suitable for rendering as an image.""" diff --git a/src/openai/types/image_gen_partial_image_event.py b/src/openai/types/image_gen_partial_image_event.py index 965d450604..df29c00a63 100644 --- a/src/openai/types/image_gen_partial_image_event.py +++ b/src/openai/types/image_gen_partial_image_event.py @@ -8,6 +8,8 @@ class ImageGenPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image generation streaming.""" + b64_json: str """Base64-encoded partial image data, suitable for rendering as an image.""" diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py index 89cc71df24..914017823e 100644 --- a/src/openai/types/images_response.py +++ b/src/openai/types/images_response.py @@ -10,6 +10,8 @@ class UsageInputTokensDetails(BaseModel): + """The input tokens detailed information for the image generation.""" + image_tokens: int """The number of image tokens in the input prompt.""" @@ -18,6 +20,8 @@ class UsageInputTokensDetails(BaseModel): class Usage(BaseModel): + """For `gpt-image-1` only, the token usage information for the image generation.""" + input_tokens: int """The number of tokens (images and text) in the input prompt.""" @@ -32,6 +36,8 @@ class Usage(BaseModel): class ImagesResponse(BaseModel): + """The response from the image generation endpoint.""" + created: int """The Unix timestamp (in seconds) of when the image was created.""" diff --git a/src/openai/types/model.py b/src/openai/types/model.py index 2631ee8d1a..6506224a20 100644 --- a/src/openai/types/model.py +++ b/src/openai/types/model.py @@ -8,6 +8,8 @@ class Model(BaseModel): + """Describes an OpenAI model offering that can be used with the API.""" + id: str """The model identifier, which can be referenced in the API endpoints.""" diff --git a/src/openai/types/moderation.py b/src/openai/types/moderation.py index 608f562218..a6acc26db4 100644 --- a/src/openai/types/moderation.py +++ b/src/openai/types/moderation.py @@ -11,6 +11,8 @@ class Categories(BaseModel): + """A list of the categories, and whether they are flagged or not.""" + harassment: bool """ Content that expresses, incites, or promotes harassing language towards any @@ -89,6 +91,10 @@ class Categories(BaseModel): class CategoryAppliedInputTypes(BaseModel): + """ + A list of the categories along with the input type(s) that the score applies to. + """ + harassment: List[Literal["text"]] """The applied input type(s) for the category 'harassment'.""" @@ -130,6 +136,8 @@ class CategoryAppliedInputTypes(BaseModel): class CategoryScores(BaseModel): + """A list of the categories along with their scores as predicted by model.""" + harassment: float """The score for the category 'harassment'.""" diff --git a/src/openai/types/moderation_create_response.py b/src/openai/types/moderation_create_response.py index 79684f8a70..23c03875bf 100644 --- a/src/openai/types/moderation_create_response.py +++ b/src/openai/types/moderation_create_response.py @@ -9,6 +9,8 @@ class ModerationCreateResponse(BaseModel): + """Represents if a given text input is potentially harmful.""" + id: str """The unique identifier for the moderation request.""" diff --git a/src/openai/types/moderation_image_url_input_param.py b/src/openai/types/moderation_image_url_input_param.py index 9a69a6a257..9c0fe25685 100644 --- a/src/openai/types/moderation_image_url_input_param.py +++ b/src/openai/types/moderation_image_url_input_param.py @@ -8,11 +8,15 @@ class ImageURL(TypedDict, total=False): + """Contains either an image URL or a data URL for a base64 encoded image.""" + url: Required[str] """Either a URL of the image or the base64 encoded image data.""" class ModerationImageURLInputParam(TypedDict, total=False): + """An object describing an image to classify.""" + image_url: Required[ImageURL] """Contains either an image URL or a data URL for a base64 encoded image.""" diff --git a/src/openai/types/moderation_text_input_param.py b/src/openai/types/moderation_text_input_param.py index e5da53337b..786ecbe625 100644 --- a/src/openai/types/moderation_text_input_param.py +++ b/src/openai/types/moderation_text_input_param.py @@ -8,6 +8,8 @@ class ModerationTextInputParam(TypedDict, total=False): + """An object describing text to classify.""" + text: Required[str] """A string of text to classify.""" diff --git a/src/openai/types/other_file_chunking_strategy_object.py b/src/openai/types/other_file_chunking_strategy_object.py index e4cd61a8fc..a5371425d7 100644 --- a/src/openai/types/other_file_chunking_strategy_object.py +++ b/src/openai/types/other_file_chunking_strategy_object.py @@ -8,5 +8,10 @@ class OtherFileChunkingStrategyObject(BaseModel): + """This is returned when the chunking strategy is unknown. + + Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API. + """ + type: Literal["other"] """Always `other`.""" diff --git a/src/openai/types/realtime/client_secret_create_params.py b/src/openai/types/realtime/client_secret_create_params.py index 5f0b0d796f..2297f3f6d2 100644 --- a/src/openai/types/realtime/client_secret_create_params.py +++ b/src/openai/types/realtime/client_secret_create_params.py @@ -28,6 +28,14 @@ class ClientSecretCreateParams(TypedDict, total=False): class ExpiresAfter(TypedDict, total=False): + """Configuration for the client secret expiration. + + Expiration refers to the time after which + a client secret will no longer be valid for creating sessions. The session itself may + continue after that time once started. A secret can be used to create multiple sessions + until it expires. + """ + anchor: Literal["created_at"] """ The anchor point for the client secret expiration, meaning that `seconds` will diff --git a/src/openai/types/realtime/client_secret_create_response.py b/src/openai/types/realtime/client_secret_create_response.py index 2aed66a25b..3a30b10544 100644 --- a/src/openai/types/realtime/client_secret_create_response.py +++ b/src/openai/types/realtime/client_secret_create_response.py @@ -16,6 +16,8 @@ class ClientSecretCreateResponse(BaseModel): + """Response from creating a session and client secret for the Realtime API.""" + expires_at: int """Expiration timestamp for the client secret, in seconds since epoch.""" diff --git a/src/openai/types/realtime/conversation_created_event.py b/src/openai/types/realtime/conversation_created_event.py index 6ec1dc8c85..3026322e86 100644 --- a/src/openai/types/realtime/conversation_created_event.py +++ b/src/openai/types/realtime/conversation_created_event.py @@ -9,6 +9,8 @@ class Conversation(BaseModel): + """The conversation resource.""" + id: Optional[str] = None """The unique ID of the conversation.""" @@ -17,6 +19,8 @@ class Conversation(BaseModel): class ConversationCreatedEvent(BaseModel): + """Returned when a conversation is created. Emitted right after session creation.""" + conversation: Conversation """The conversation resource.""" diff --git a/src/openai/types/realtime/conversation_item_added.py b/src/openai/types/realtime/conversation_item_added.py index ae9f6803e4..0e336a9261 100644 --- a/src/openai/types/realtime/conversation_item_added.py +++ b/src/openai/types/realtime/conversation_item_added.py @@ -10,6 +10,16 @@ class ConversationItemAdded(BaseModel): + """Sent by the server when an Item is added to the default Conversation. + + This can happen in several cases: + - When the client sends a `conversation.item.create` event. + - When the input audio buffer is committed. In this case the item will be a user message containing the audio from the buffer. + - When the model is generating a Response. In this case the `conversation.item.added` event will be sent when the model starts generating a specific Item, and thus it will not yet have any content (and `status` will be `in_progress`). + + The event will include the full content of the Item (except when model is generating a Response) except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if necessary. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/conversation_item_create_event.py b/src/openai/types/realtime/conversation_item_create_event.py index 8fa2dfe08c..bf2d129744 100644 --- a/src/openai/types/realtime/conversation_item_create_event.py +++ b/src/openai/types/realtime/conversation_item_create_event.py @@ -10,6 +10,16 @@ class ConversationItemCreateEvent(BaseModel): + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + item: ConversationItem """A single item within a Realtime conversation.""" diff --git a/src/openai/types/realtime/conversation_item_create_event_param.py b/src/openai/types/realtime/conversation_item_create_event_param.py index 8530dc72cd..be7f0ff011 100644 --- a/src/openai/types/realtime/conversation_item_create_event_param.py +++ b/src/openai/types/realtime/conversation_item_create_event_param.py @@ -10,6 +10,16 @@ class ConversationItemCreateEventParam(TypedDict, total=False): + """ + Add a new Item to the Conversation's context, including messages, function + calls, and function call responses. This event can be used both to populate a + "history" of the conversation and to add new items mid-stream, but has the + current limitation that it cannot populate assistant audio messages. + + If successful, the server will respond with a `conversation.item.created` + event, otherwise an `error` event will be sent. + """ + item: Required[ConversationItemParam] """A single item within a Realtime conversation.""" diff --git a/src/openai/types/realtime/conversation_item_created_event.py b/src/openai/types/realtime/conversation_item_created_event.py index 13f24ad31a..6ae6f05ffe 100644 --- a/src/openai/types/realtime/conversation_item_created_event.py +++ b/src/openai/types/realtime/conversation_item_created_event.py @@ -10,6 +10,19 @@ class ConversationItemCreatedEvent(BaseModel): + """Returned when a conversation item is created. + + There are several scenarios that produce this event: + - The server is generating a Response, which if successful will produce + either one or two Items, which will be of type `message` + (role `assistant`) or type `function_call`. + - The input audio buffer has been committed, either by the client or the + server (in `server_vad` mode). The server will take the content of the + input audio buffer and add it to a new user message Item. + - The client has sent a `conversation.item.create` event to add a new Item + to the Conversation. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/conversation_item_delete_event.py b/src/openai/types/realtime/conversation_item_delete_event.py index 3734f72e9d..c662f386e3 100644 --- a/src/openai/types/realtime/conversation_item_delete_event.py +++ b/src/openai/types/realtime/conversation_item_delete_event.py @@ -9,6 +9,14 @@ class ConversationItemDeleteEvent(BaseModel): + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + item_id: str """The ID of the item to delete.""" diff --git a/src/openai/types/realtime/conversation_item_delete_event_param.py b/src/openai/types/realtime/conversation_item_delete_event_param.py index c3f88d6627..e79bb68c9a 100644 --- a/src/openai/types/realtime/conversation_item_delete_event_param.py +++ b/src/openai/types/realtime/conversation_item_delete_event_param.py @@ -8,6 +8,14 @@ class ConversationItemDeleteEventParam(TypedDict, total=False): + """Send this event when you want to remove any item from the conversation + history. + + The server will respond with a `conversation.item.deleted` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + item_id: Required[str] """The ID of the item to delete.""" diff --git a/src/openai/types/realtime/conversation_item_deleted_event.py b/src/openai/types/realtime/conversation_item_deleted_event.py index cfe6fe85fc..9826289ebf 100644 --- a/src/openai/types/realtime/conversation_item_deleted_event.py +++ b/src/openai/types/realtime/conversation_item_deleted_event.py @@ -8,6 +8,12 @@ class ConversationItemDeletedEvent(BaseModel): + """ + Returned when an item in the conversation is deleted by the client with a + `conversation.item.delete` event. This event is used to synchronize the + server's understanding of the conversation history with the client's view. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/conversation_item_done.py b/src/openai/types/realtime/conversation_item_done.py index a4c9b8a840..6a823c65a8 100644 --- a/src/openai/types/realtime/conversation_item_done.py +++ b/src/openai/types/realtime/conversation_item_done.py @@ -10,6 +10,11 @@ class ConversationItemDone(BaseModel): + """Returned when a conversation item is finalized. + + The event will include the full content of the Item except for audio data, which can be retrieved separately with a `conversation.item.retrieve` event if needed. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py index 09b20aa184..3304233f8f 100644 --- a/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_completed_event.py @@ -16,6 +16,8 @@ class UsageTranscriptTextUsageTokensInputTokenDetails(BaseModel): + """Details about the input tokens billed for this request.""" + audio_tokens: Optional[int] = None """Number of audio tokens billed for this request.""" @@ -24,6 +26,8 @@ class UsageTranscriptTextUsageTokensInputTokenDetails(BaseModel): class UsageTranscriptTextUsageTokens(BaseModel): + """Usage statistics for models billed by token usage.""" + input_tokens: int """Number of input tokens billed for this request.""" @@ -41,6 +45,8 @@ class UsageTranscriptTextUsageTokens(BaseModel): class UsageTranscriptTextUsageDuration(BaseModel): + """Usage statistics for models billed by audio input duration.""" + seconds: float """Duration of the input audio in seconds.""" @@ -52,6 +58,19 @@ class UsageTranscriptTextUsageDuration(BaseModel): class ConversationItemInputAudioTranscriptionCompletedEvent(BaseModel): + """ + This event is the output of audio transcription for user audio written to the + user audio buffer. Transcription begins when the input audio buffer is + committed by the client or server (when VAD is enabled). Transcription runs + asynchronously with Response creation, so this event may come before or after + the Response events. + + Realtime API models accept audio natively, and thus input transcription is a + separate process run on a separate ASR (Automatic Speech Recognition) model. + The transcript may diverge somewhat from the model's interpretation, and + should be treated as a rough guide. + """ + content_index: int """The index of the content part containing the audio.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py index f49e6f636f..5f3f54810f 100644 --- a/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py @@ -10,6 +10,10 @@ class ConversationItemInputAudioTranscriptionDeltaEvent(BaseModel): + """ + Returned when the text value of an input audio transcription content part is updated with incremental transcription results. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py index edb97bbf6f..e8ad05e43c 100644 --- a/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_failed_event.py @@ -9,6 +9,8 @@ class Error(BaseModel): + """Details of the transcription error.""" + code: Optional[str] = None """Error code, if any.""" @@ -23,6 +25,12 @@ class Error(BaseModel): class ConversationItemInputAudioTranscriptionFailedEvent(BaseModel): + """ + Returned when input audio transcription is configured, and a transcription + request for a user message failed. These events are separate from other + `error` events so that the client can identify the related Item. + """ + content_index: int """The index of the content part containing the audio.""" diff --git a/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py b/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py index e2cbc9d299..dcc4916580 100644 --- a/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py +++ b/src/openai/types/realtime/conversation_item_input_audio_transcription_segment.py @@ -8,6 +8,8 @@ class ConversationItemInputAudioTranscriptionSegment(BaseModel): + """Returned when an input audio transcription segment is identified for an item.""" + id: str """The segment identifier.""" diff --git a/src/openai/types/realtime/conversation_item_retrieve_event.py b/src/openai/types/realtime/conversation_item_retrieve_event.py index 018c2ccc59..e7d8eb6c49 100644 --- a/src/openai/types/realtime/conversation_item_retrieve_event.py +++ b/src/openai/types/realtime/conversation_item_retrieve_event.py @@ -9,6 +9,13 @@ class ConversationItemRetrieveEvent(BaseModel): + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + item_id: str """The ID of the item to retrieve.""" diff --git a/src/openai/types/realtime/conversation_item_retrieve_event_param.py b/src/openai/types/realtime/conversation_item_retrieve_event_param.py index 71b3ffa499..59fdb6fb93 100644 --- a/src/openai/types/realtime/conversation_item_retrieve_event_param.py +++ b/src/openai/types/realtime/conversation_item_retrieve_event_param.py @@ -8,6 +8,13 @@ class ConversationItemRetrieveEventParam(TypedDict, total=False): + """ + Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD. + The server will respond with a `conversation.item.retrieved` event, + unless the item does not exist in the conversation history, in which case the + server will respond with an error. + """ + item_id: Required[str] """The ID of the item to retrieve.""" diff --git a/src/openai/types/realtime/conversation_item_truncate_event.py b/src/openai/types/realtime/conversation_item_truncate_event.py index d6c6779cc8..16c82183c4 100644 --- a/src/openai/types/realtime/conversation_item_truncate_event.py +++ b/src/openai/types/realtime/conversation_item_truncate_event.py @@ -9,6 +9,21 @@ class ConversationItemTruncateEvent(BaseModel): + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + audio_end_ms: int """Inclusive duration up to which audio is truncated, in milliseconds. diff --git a/src/openai/types/realtime/conversation_item_truncate_event_param.py b/src/openai/types/realtime/conversation_item_truncate_event_param.py index f5ab13a419..e9b41fc980 100644 --- a/src/openai/types/realtime/conversation_item_truncate_event_param.py +++ b/src/openai/types/realtime/conversation_item_truncate_event_param.py @@ -8,6 +8,21 @@ class ConversationItemTruncateEventParam(TypedDict, total=False): + """Send this event to truncate a previous assistant message’s audio. + + The server + will produce audio faster than realtime, so this event is useful when the user + interrupts to truncate audio that has already been sent to the client but not + yet played. This will synchronize the server's understanding of the audio with + the client's playback. + + Truncating audio will delete the server-side text transcript to ensure there + is not text in the context that hasn't been heard by the user. + + If successful, the server will respond with a `conversation.item.truncated` + event. + """ + audio_end_ms: Required[int] """Inclusive duration up to which audio is truncated, in milliseconds. diff --git a/src/openai/types/realtime/conversation_item_truncated_event.py b/src/openai/types/realtime/conversation_item_truncated_event.py index f56cabc3d9..c78a776d9b 100644 --- a/src/openai/types/realtime/conversation_item_truncated_event.py +++ b/src/openai/types/realtime/conversation_item_truncated_event.py @@ -8,6 +8,15 @@ class ConversationItemTruncatedEvent(BaseModel): + """ + Returned when an earlier assistant audio message item is truncated by the + client with a `conversation.item.truncate` event. This event is used to + synchronize the server's understanding of the audio with the client's playback. + + This action will truncate the audio and remove the server-side text transcript + to ensure there is no text in the context that hasn't been heard by the user. + """ + audio_end_ms: int """The duration up to which the audio was truncated, in milliseconds.""" diff --git a/src/openai/types/realtime/input_audio_buffer_append_event.py b/src/openai/types/realtime/input_audio_buffer_append_event.py index 8562cf0af4..4c9e9a544d 100644 --- a/src/openai/types/realtime/input_audio_buffer_append_event.py +++ b/src/openai/types/realtime/input_audio_buffer_append_event.py @@ -9,6 +9,23 @@ class InputAudioBufferAppendEvent(BaseModel): + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. Input audio noise reduction operates on writes to the audio buffer. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike most other client events, the server will + not send a confirmation response to this event. + """ + audio: str """Base64-encoded audio bytes. diff --git a/src/openai/types/realtime/input_audio_buffer_append_event_param.py b/src/openai/types/realtime/input_audio_buffer_append_event_param.py index 3ad0bc737d..a0d308e4d9 100644 --- a/src/openai/types/realtime/input_audio_buffer_append_event_param.py +++ b/src/openai/types/realtime/input_audio_buffer_append_event_param.py @@ -8,6 +8,23 @@ class InputAudioBufferAppendEventParam(TypedDict, total=False): + """Send this event to append audio bytes to the input audio buffer. + + The audio + buffer is temporary storage you can write to and later commit. A "commit" will create a new + user message item in the conversation history from the buffer content and clear the buffer. + Input audio transcription (if enabled) will be generated when the buffer is committed. + + If VAD is enabled the audio buffer is used to detect speech and the server will decide + when to commit. When Server VAD is disabled, you must commit the audio buffer + manually. Input audio noise reduction operates on writes to the audio buffer. + + The client may choose how much audio to place in each event up to a maximum + of 15 MiB, for example streaming smaller chunks from the client may allow the + VAD to be more responsive. Unlike most other client events, the server will + not send a confirmation response to this event. + """ + audio: Required[str] """Base64-encoded audio bytes. diff --git a/src/openai/types/realtime/input_audio_buffer_clear_event.py b/src/openai/types/realtime/input_audio_buffer_clear_event.py index 9922ff3b32..5526bcbfa9 100644 --- a/src/openai/types/realtime/input_audio_buffer_clear_event.py +++ b/src/openai/types/realtime/input_audio_buffer_clear_event.py @@ -9,6 +9,12 @@ class InputAudioBufferClearEvent(BaseModel): + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + type: Literal["input_audio_buffer.clear"] """The event type, must be `input_audio_buffer.clear`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_clear_event_param.py b/src/openai/types/realtime/input_audio_buffer_clear_event_param.py index 2bd6bc5a02..8e0e9c55fa 100644 --- a/src/openai/types/realtime/input_audio_buffer_clear_event_param.py +++ b/src/openai/types/realtime/input_audio_buffer_clear_event_param.py @@ -8,6 +8,12 @@ class InputAudioBufferClearEventParam(TypedDict, total=False): + """Send this event to clear the audio bytes in the buffer. + + The server will + respond with an `input_audio_buffer.cleared` event. + """ + type: Required[Literal["input_audio_buffer.clear"]] """The event type, must be `input_audio_buffer.clear`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_cleared_event.py b/src/openai/types/realtime/input_audio_buffer_cleared_event.py index af71844f2f..e4775567dc 100644 --- a/src/openai/types/realtime/input_audio_buffer_cleared_event.py +++ b/src/openai/types/realtime/input_audio_buffer_cleared_event.py @@ -8,6 +8,11 @@ class InputAudioBufferClearedEvent(BaseModel): + """ + Returned when the input audio buffer is cleared by the client with a + `input_audio_buffer.clear` event. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_commit_event.py b/src/openai/types/realtime/input_audio_buffer_commit_event.py index 125c3ba1e8..fe2ec01783 100644 --- a/src/openai/types/realtime/input_audio_buffer_commit_event.py +++ b/src/openai/types/realtime/input_audio_buffer_commit_event.py @@ -9,6 +9,12 @@ class InputAudioBufferCommitEvent(BaseModel): + """ + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. + + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. + """ + type: Literal["input_audio_buffer.commit"] """The event type, must be `input_audio_buffer.commit`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_commit_event_param.py b/src/openai/types/realtime/input_audio_buffer_commit_event_param.py index c9c927ab98..20342795e8 100644 --- a/src/openai/types/realtime/input_audio_buffer_commit_event_param.py +++ b/src/openai/types/realtime/input_audio_buffer_commit_event_param.py @@ -8,6 +8,12 @@ class InputAudioBufferCommitEventParam(TypedDict, total=False): + """ + Send this event to commit the user input audio buffer, which will create a new user message item in the conversation. This event will produce an error if the input audio buffer is empty. When in Server VAD mode, the client does not need to send this event, the server will commit the audio buffer automatically. + + Committing the input audio buffer will trigger input audio transcription (if enabled in session configuration), but it will not create a response from the model. The server will respond with an `input_audio_buffer.committed` event. + """ + type: Required[Literal["input_audio_buffer.commit"]] """The event type, must be `input_audio_buffer.commit`.""" diff --git a/src/openai/types/realtime/input_audio_buffer_committed_event.py b/src/openai/types/realtime/input_audio_buffer_committed_event.py index 5ed1b4ccc7..15dc8254f3 100644 --- a/src/openai/types/realtime/input_audio_buffer_committed_event.py +++ b/src/openai/types/realtime/input_audio_buffer_committed_event.py @@ -9,6 +9,13 @@ class InputAudioBufferCommittedEvent(BaseModel): + """ + Returned when an input audio buffer is committed, either by the client or + automatically in server VAD mode. The `item_id` property is the ID of the user + message item that will be created, thus a `conversation.item.created` event + will also be sent to the client. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py b/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py index d61ed4bda7..c2623cc7b8 100644 --- a/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py +++ b/src/openai/types/realtime/input_audio_buffer_dtmf_event_received_event.py @@ -8,6 +8,14 @@ class InputAudioBufferDtmfEventReceivedEvent(BaseModel): + """**SIP Only:** Returned when an DTMF event is received. + + A DTMF event is a message that + represents a telephone keypad press (0–9, *, #, A–D). The `event` property + is the keypad that the user press. The `received_at` is the UTC Unix Timestamp + that the server received the event. + """ + event: str """The telephone keypad that was pressed by the user.""" diff --git a/src/openai/types/realtime/input_audio_buffer_speech_started_event.py b/src/openai/types/realtime/input_audio_buffer_speech_started_event.py index 865205d786..1bd4c74eb0 100644 --- a/src/openai/types/realtime/input_audio_buffer_speech_started_event.py +++ b/src/openai/types/realtime/input_audio_buffer_speech_started_event.py @@ -8,6 +8,19 @@ class InputAudioBufferSpeechStartedEvent(BaseModel): + """ + Sent by the server when in `server_vad` mode to indicate that speech has been + detected in the audio buffer. This can happen any time audio is added to the + buffer (unless speech is already detected). The client may want to use this + event to interrupt audio playback or provide visual feedback to the user. + + The client should expect to receive a `input_audio_buffer.speech_stopped` event + when speech stops. The `item_id` property is the ID of the user message item + that will be created when speech stops and will also be included in the + `input_audio_buffer.speech_stopped` event (unless the client manually commits + the audio buffer during VAD activation). + """ + audio_start_ms: int """ Milliseconds from the start of all audio written to the buffer during the diff --git a/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py b/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py index 6cb7845ff4..b3fb20929a 100644 --- a/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py +++ b/src/openai/types/realtime/input_audio_buffer_speech_stopped_event.py @@ -8,6 +8,12 @@ class InputAudioBufferSpeechStoppedEvent(BaseModel): + """ + Returned in `server_vad` mode when the server detects the end of speech in + the audio buffer. The server will also send an `conversation.item.created` + event with the user message item that is created from the audio buffer. + """ + audio_end_ms: int """Milliseconds since the session started when speech stopped. diff --git a/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py index 5c5dc5cfa6..72b107d56e 100644 --- a/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py +++ b/src/openai/types/realtime/input_audio_buffer_timeout_triggered.py @@ -8,6 +8,23 @@ class InputAudioBufferTimeoutTriggered(BaseModel): + """Returned when the Server VAD timeout is triggered for the input audio buffer. + + This is configured + with `idle_timeout_ms` in the `turn_detection` settings of the session, and it indicates that + there hasn't been any speech detected for the configured duration. + + The `audio_start_ms` and `audio_end_ms` fields indicate the segment of audio after the last + model response up to the triggering time, as an offset from the beginning of audio written + to the input audio buffer. This means it demarcates the segment of audio that was silent and + the difference between the start and end values will roughly match the configured timeout. + + The empty audio will be committed to the conversation as an `input_audio` item (there will be a + `input_audio_buffer.committed` event) and a model response will be generated. There may be speech + that didn't trigger VAD but is still detected by the model, so the model may respond with + something relevant to the conversation or a prompt to continue speaking. + """ + audio_end_ms: int """ Millisecond offset of audio written to the input audio buffer at the time the diff --git a/src/openai/types/realtime/log_prob_properties.py b/src/openai/types/realtime/log_prob_properties.py index 92477d67d0..423af1c492 100644 --- a/src/openai/types/realtime/log_prob_properties.py +++ b/src/openai/types/realtime/log_prob_properties.py @@ -8,6 +8,8 @@ class LogProbProperties(BaseModel): + """A log probability object.""" + token: str """The token that was used to generate the log probability.""" diff --git a/src/openai/types/realtime/mcp_list_tools_completed.py b/src/openai/types/realtime/mcp_list_tools_completed.py index 941280f01a..2fe64147d6 100644 --- a/src/openai/types/realtime/mcp_list_tools_completed.py +++ b/src/openai/types/realtime/mcp_list_tools_completed.py @@ -8,6 +8,8 @@ class McpListToolsCompleted(BaseModel): + """Returned when listing MCP tools has completed for an item.""" + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/mcp_list_tools_failed.py b/src/openai/types/realtime/mcp_list_tools_failed.py index 892eda21bd..8cad7c0a12 100644 --- a/src/openai/types/realtime/mcp_list_tools_failed.py +++ b/src/openai/types/realtime/mcp_list_tools_failed.py @@ -8,6 +8,8 @@ class McpListToolsFailed(BaseModel): + """Returned when listing MCP tools has failed for an item.""" + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/mcp_list_tools_in_progress.py b/src/openai/types/realtime/mcp_list_tools_in_progress.py index 4254b5fd33..823bb875a3 100644 --- a/src/openai/types/realtime/mcp_list_tools_in_progress.py +++ b/src/openai/types/realtime/mcp_list_tools_in_progress.py @@ -8,6 +8,8 @@ class McpListToolsInProgress(BaseModel): + """Returned when listing MCP tools is in progress for an item.""" + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event.py b/src/openai/types/realtime/output_audio_buffer_clear_event.py index b4c95039f3..b3fa7620ac 100644 --- a/src/openai/types/realtime/output_audio_buffer_clear_event.py +++ b/src/openai/types/realtime/output_audio_buffer_clear_event.py @@ -9,6 +9,15 @@ class OutputAudioBufferClearEvent(BaseModel): + """**WebRTC/SIP Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + type: Literal["output_audio_buffer.clear"] """The event type, must be `output_audio_buffer.clear`.""" diff --git a/src/openai/types/realtime/output_audio_buffer_clear_event_param.py b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py index a3205ebc6c..59f897a5c1 100644 --- a/src/openai/types/realtime/output_audio_buffer_clear_event_param.py +++ b/src/openai/types/realtime/output_audio_buffer_clear_event_param.py @@ -8,6 +8,15 @@ class OutputAudioBufferClearEventParam(TypedDict, total=False): + """**WebRTC/SIP Only:** Emit to cut off the current audio response. + + This will trigger the server to + stop generating audio and emit a `output_audio_buffer.cleared` event. This + event should be preceded by a `response.cancel` client event to stop the + generation of the current response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + type: Required[Literal["output_audio_buffer.clear"]] """The event type, must be `output_audio_buffer.clear`.""" diff --git a/src/openai/types/realtime/rate_limits_updated_event.py b/src/openai/types/realtime/rate_limits_updated_event.py index 048a4028a1..951de103af 100644 --- a/src/openai/types/realtime/rate_limits_updated_event.py +++ b/src/openai/types/realtime/rate_limits_updated_event.py @@ -23,6 +23,14 @@ class RateLimit(BaseModel): class RateLimitsUpdatedEvent(BaseModel): + """Emitted at the beginning of a Response to indicate the updated rate limits. + + + When a Response is created some tokens will be "reserved" for the output + tokens, the rate limits shown here reflect that reservation, which is then + adjusted accordingly once the Response is completed. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/realtime_audio_config.py b/src/openai/types/realtime/realtime_audio_config.py index 72d7cc59cc..daa50358a8 100644 --- a/src/openai/types/realtime/realtime_audio_config.py +++ b/src/openai/types/realtime/realtime_audio_config.py @@ -10,6 +10,8 @@ class RealtimeAudioConfig(BaseModel): + """Configuration for input and output audio.""" + input: Optional[RealtimeAudioConfigInput] = None output: Optional[RealtimeAudioConfigOutput] = None diff --git a/src/openai/types/realtime/realtime_audio_config_input.py b/src/openai/types/realtime/realtime_audio_config_input.py index cfcb7f22d4..08e1b14601 100644 --- a/src/openai/types/realtime/realtime_audio_config_input.py +++ b/src/openai/types/realtime/realtime_audio_config_input.py @@ -12,6 +12,13 @@ class NoiseReduction(BaseModel): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + type: Optional[NoiseReductionType] = None """Type of noise reduction. diff --git a/src/openai/types/realtime/realtime_audio_config_input_param.py b/src/openai/types/realtime/realtime_audio_config_input_param.py index 730f46cfec..73495e6cd3 100644 --- a/src/openai/types/realtime/realtime_audio_config_input_param.py +++ b/src/openai/types/realtime/realtime_audio_config_input_param.py @@ -14,6 +14,13 @@ class NoiseReduction(TypedDict, total=False): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + type: NoiseReductionType """Type of noise reduction. diff --git a/src/openai/types/realtime/realtime_audio_config_param.py b/src/openai/types/realtime/realtime_audio_config_param.py index 2c41de35ae..7899fe359b 100644 --- a/src/openai/types/realtime/realtime_audio_config_param.py +++ b/src/openai/types/realtime/realtime_audio_config_param.py @@ -11,6 +11,8 @@ class RealtimeAudioConfigParam(TypedDict, total=False): + """Configuration for input and output audio.""" + input: RealtimeAudioConfigInputParam output: RealtimeAudioConfigOutputParam diff --git a/src/openai/types/realtime/realtime_audio_formats.py b/src/openai/types/realtime/realtime_audio_formats.py index 10f91883b6..fa10c9a7a4 100644 --- a/src/openai/types/realtime/realtime_audio_formats.py +++ b/src/openai/types/realtime/realtime_audio_formats.py @@ -10,6 +10,8 @@ class AudioPCM(BaseModel): + """The PCM audio format. Only a 24kHz sample rate is supported.""" + rate: Optional[Literal[24000]] = None """The sample rate of the audio. Always `24000`.""" @@ -18,11 +20,15 @@ class AudioPCM(BaseModel): class AudioPCMU(BaseModel): + """The G.711 μ-law format.""" + type: Optional[Literal["audio/pcmu"]] = None """The audio format. Always `audio/pcmu`.""" class AudioPCMA(BaseModel): + """The G.711 A-law format.""" + type: Optional[Literal["audio/pcma"]] = None """The audio format. Always `audio/pcma`.""" diff --git a/src/openai/types/realtime/realtime_audio_formats_param.py b/src/openai/types/realtime/realtime_audio_formats_param.py index cf58577f38..6392f632c3 100644 --- a/src/openai/types/realtime/realtime_audio_formats_param.py +++ b/src/openai/types/realtime/realtime_audio_formats_param.py @@ -9,6 +9,8 @@ class AudioPCM(TypedDict, total=False): + """The PCM audio format. Only a 24kHz sample rate is supported.""" + rate: Literal[24000] """The sample rate of the audio. Always `24000`.""" @@ -17,11 +19,15 @@ class AudioPCM(TypedDict, total=False): class AudioPCMU(TypedDict, total=False): + """The G.711 μ-law format.""" + type: Literal["audio/pcmu"] """The audio format. Always `audio/pcmu`.""" class AudioPCMA(TypedDict, total=False): + """The G.711 A-law format.""" + type: Literal["audio/pcma"] """The audio format. Always `audio/pcma`.""" diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_audio_input_turn_detection.py index 9b55353884..8d9aff3563 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection.py @@ -10,6 +10,10 @@ class ServerVad(BaseModel): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + type: Literal["server_vad"] """Type of turn detection, `server_vad` to turn on simple Server VAD.""" @@ -76,6 +80,10 @@ class ServerVad(BaseModel): class SemanticVad(BaseModel): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + type: Literal["semantic_vad"] """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" diff --git a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py index 4ce7640727..30522d74e1 100644 --- a/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py +++ b/src/openai/types/realtime/realtime_audio_input_turn_detection_param.py @@ -9,6 +9,10 @@ class ServerVad(TypedDict, total=False): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + type: Required[Literal["server_vad"]] """Type of turn detection, `server_vad` to turn on simple Server VAD.""" @@ -75,6 +79,10 @@ class ServerVad(TypedDict, total=False): class SemanticVad(TypedDict, total=False): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + type: Required[Literal["semantic_vad"]] """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py index 6b0f86ee32..207831a3c8 100644 --- a/src/openai/types/realtime/realtime_conversation_item_assistant_message.py +++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message.py @@ -33,6 +33,8 @@ class Content(BaseModel): class RealtimeConversationItemAssistantMessage(BaseModel): + """An assistant message item in a Realtime conversation.""" + content: List[Content] """The content of the message.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py index 93699afba2..abc78e7d3f 100644 --- a/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_assistant_message_param.py @@ -33,6 +33,8 @@ class Content(TypedDict, total=False): class RealtimeConversationItemAssistantMessageParam(TypedDict, total=False): + """An assistant message item in a Realtime conversation.""" + content: Required[Iterable[Content]] """The content of the message.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call.py b/src/openai/types/realtime/realtime_conversation_item_function_call.py index 279a2fcdc5..4e40394883 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call.py @@ -9,6 +9,8 @@ class RealtimeConversationItemFunctionCall(BaseModel): + """A function call item in a Realtime conversation.""" + arguments: str """The arguments of the function call. diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py index 4b6b15d0ad..cdbc352d85 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call_output.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output.py @@ -9,6 +9,8 @@ class RealtimeConversationItemFunctionCallOutput(BaseModel): + """A function call output item in a Realtime conversation.""" + call_id: str """The ID of the function call this output is for.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py index 56d62da563..2e56a81dc3 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_output_param.py @@ -8,6 +8,8 @@ class RealtimeConversationItemFunctionCallOutputParam(TypedDict, total=False): + """A function call output item in a Realtime conversation.""" + call_id: Required[str] """The ID of the function call this output is for.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_function_call_param.py b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py index 36a16a27b3..6467ce149e 100644 --- a/src/openai/types/realtime/realtime_conversation_item_function_call_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_function_call_param.py @@ -8,6 +8,8 @@ class RealtimeConversationItemFunctionCallParam(TypedDict, total=False): + """A function call item in a Realtime conversation.""" + arguments: Required[str] """The arguments of the function call. diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message.py b/src/openai/types/realtime/realtime_conversation_item_system_message.py index 7dac5c9fe2..f69bc03937 100644 --- a/src/openai/types/realtime/realtime_conversation_item_system_message.py +++ b/src/openai/types/realtime/realtime_conversation_item_system_message.py @@ -17,6 +17,10 @@ class Content(BaseModel): class RealtimeConversationItemSystemMessage(BaseModel): + """ + A system message in a Realtime conversation can be used to provide additional context or instructions to the model. This is similar but distinct from the instruction prompt provided at the start of a conversation, as system messages can be added at any point in the conversation. For major changes to the conversation's behavior, use instructions, but for smaller updates (e.g. "the user is now asking about a different topic"), use system messages. + """ + content: List[Content] """The content of the message.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_system_message_param.py b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py index a2790fcf67..93880198fa 100644 --- a/src/openai/types/realtime/realtime_conversation_item_system_message_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_system_message_param.py @@ -17,6 +17,10 @@ class Content(TypedDict, total=False): class RealtimeConversationItemSystemMessageParam(TypedDict, total=False): + """ + A system message in a Realtime conversation can be used to provide additional context or instructions to the model. This is similar but distinct from the instruction prompt provided at the start of a conversation, as system messages can be added at any point in the conversation. For major changes to the conversation's behavior, use instructions, but for smaller updates (e.g. "the user is now asking about a different topic"), use system messages. + """ + content: Required[Iterable[Content]] """The content of the message.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message.py b/src/openai/types/realtime/realtime_conversation_item_user_message.py index 30d9bb10e3..20e9614eb6 100644 --- a/src/openai/types/realtime/realtime_conversation_item_user_message.py +++ b/src/openai/types/realtime/realtime_conversation_item_user_message.py @@ -44,6 +44,8 @@ class Content(BaseModel): class RealtimeConversationItemUserMessage(BaseModel): + """A user message item in a Realtime conversation.""" + content: List[Content] """The content of the message.""" diff --git a/src/openai/types/realtime/realtime_conversation_item_user_message_param.py b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py index 7d3b9bc137..69a24692e8 100644 --- a/src/openai/types/realtime/realtime_conversation_item_user_message_param.py +++ b/src/openai/types/realtime/realtime_conversation_item_user_message_param.py @@ -44,6 +44,8 @@ class Content(TypedDict, total=False): class RealtimeConversationItemUserMessageParam(TypedDict, total=False): + """A user message item in a Realtime conversation.""" + content: Required[Iterable[Content]] """The content of the message.""" diff --git a/src/openai/types/realtime/realtime_error.py b/src/openai/types/realtime/realtime_error.py index f1017d09e4..2aa5bc9425 100644 --- a/src/openai/types/realtime/realtime_error.py +++ b/src/openai/types/realtime/realtime_error.py @@ -8,6 +8,8 @@ class RealtimeError(BaseModel): + """Details of the error.""" + message: str """A human-readable error message.""" diff --git a/src/openai/types/realtime/realtime_error_event.py b/src/openai/types/realtime/realtime_error_event.py index 8b501d6b21..574464b29e 100644 --- a/src/openai/types/realtime/realtime_error_event.py +++ b/src/openai/types/realtime/realtime_error_event.py @@ -9,6 +9,12 @@ class RealtimeErrorEvent(BaseModel): + """ + Returned when an error occurs, which could be a client problem or a server + problem. Most errors are recoverable and the session will stay open, we + recommend to implementors to monitor and log error messages by default. + """ + error: RealtimeError """Details of the error.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_request.py b/src/openai/types/realtime/realtime_mcp_approval_request.py index bafc8d89d4..1744c90070 100644 --- a/src/openai/types/realtime/realtime_mcp_approval_request.py +++ b/src/openai/types/realtime/realtime_mcp_approval_request.py @@ -8,6 +8,8 @@ class RealtimeMcpApprovalRequest(BaseModel): + """A Realtime item requesting human approval of a tool invocation.""" + id: str """The unique ID of the approval request.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_request_param.py b/src/openai/types/realtime/realtime_mcp_approval_request_param.py index 57c21a487f..f7cb68d67e 100644 --- a/src/openai/types/realtime/realtime_mcp_approval_request_param.py +++ b/src/openai/types/realtime/realtime_mcp_approval_request_param.py @@ -8,6 +8,8 @@ class RealtimeMcpApprovalRequestParam(TypedDict, total=False): + """A Realtime item requesting human approval of a tool invocation.""" + id: Required[str] """The unique ID of the approval request.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_response.py b/src/openai/types/realtime/realtime_mcp_approval_response.py index 2cb03bc61a..f8525a12fc 100644 --- a/src/openai/types/realtime/realtime_mcp_approval_response.py +++ b/src/openai/types/realtime/realtime_mcp_approval_response.py @@ -9,6 +9,8 @@ class RealtimeMcpApprovalResponse(BaseModel): + """A Realtime item responding to an MCP approval request.""" + id: str """The unique ID of the approval response.""" diff --git a/src/openai/types/realtime/realtime_mcp_approval_response_param.py b/src/openai/types/realtime/realtime_mcp_approval_response_param.py index 19b6337004..6a65f7ce38 100644 --- a/src/openai/types/realtime/realtime_mcp_approval_response_param.py +++ b/src/openai/types/realtime/realtime_mcp_approval_response_param.py @@ -9,6 +9,8 @@ class RealtimeMcpApprovalResponseParam(TypedDict, total=False): + """A Realtime item responding to an MCP approval request.""" + id: Required[str] """The unique ID of the approval response.""" diff --git a/src/openai/types/realtime/realtime_mcp_list_tools.py b/src/openai/types/realtime/realtime_mcp_list_tools.py index aeb58a1faf..669d1fb43b 100644 --- a/src/openai/types/realtime/realtime_mcp_list_tools.py +++ b/src/openai/types/realtime/realtime_mcp_list_tools.py @@ -9,6 +9,8 @@ class Tool(BaseModel): + """A tool available on an MCP server.""" + input_schema: object """The JSON schema describing the tool's input.""" @@ -23,6 +25,8 @@ class Tool(BaseModel): class RealtimeMcpListTools(BaseModel): + """A Realtime item listing tools available on an MCP server.""" + server_label: str """The label of the MCP server.""" diff --git a/src/openai/types/realtime/realtime_mcp_list_tools_param.py b/src/openai/types/realtime/realtime_mcp_list_tools_param.py index eb8605a061..614fa53347 100644 --- a/src/openai/types/realtime/realtime_mcp_list_tools_param.py +++ b/src/openai/types/realtime/realtime_mcp_list_tools_param.py @@ -9,6 +9,8 @@ class Tool(TypedDict, total=False): + """A tool available on an MCP server.""" + input_schema: Required[object] """The JSON schema describing the tool's input.""" @@ -23,6 +25,8 @@ class Tool(TypedDict, total=False): class RealtimeMcpListToolsParam(TypedDict, total=False): + """A Realtime item listing tools available on an MCP server.""" + server_label: Required[str] """The label of the MCP server.""" diff --git a/src/openai/types/realtime/realtime_mcp_tool_call.py b/src/openai/types/realtime/realtime_mcp_tool_call.py index 019aee25c0..f53ad0eaa9 100644 --- a/src/openai/types/realtime/realtime_mcp_tool_call.py +++ b/src/openai/types/realtime/realtime_mcp_tool_call.py @@ -18,6 +18,8 @@ class RealtimeMcpToolCall(BaseModel): + """A Realtime item representing an invocation of a tool on an MCP server.""" + id: str """The unique ID of the tool call.""" diff --git a/src/openai/types/realtime/realtime_mcp_tool_call_param.py b/src/openai/types/realtime/realtime_mcp_tool_call_param.py index 0ba16d3dc1..8ccb5efc8a 100644 --- a/src/openai/types/realtime/realtime_mcp_tool_call_param.py +++ b/src/openai/types/realtime/realtime_mcp_tool_call_param.py @@ -15,6 +15,8 @@ class RealtimeMcpToolCallParam(TypedDict, total=False): + """A Realtime item representing an invocation of a tool on an MCP server.""" + id: Required[str] """The unique ID of the tool call.""" diff --git a/src/openai/types/realtime/realtime_response.py b/src/openai/types/realtime/realtime_response.py index 92d75491c0..a23edc48ab 100644 --- a/src/openai/types/realtime/realtime_response.py +++ b/src/openai/types/realtime/realtime_response.py @@ -30,10 +30,14 @@ class AudioOutput(BaseModel): class Audio(BaseModel): + """Configuration for audio output.""" + output: Optional[AudioOutput] = None class RealtimeResponse(BaseModel): + """The response resource.""" + id: Optional[str] = None """The unique ID of the response, will look like `resp_1234`.""" diff --git a/src/openai/types/realtime/realtime_response_create_audio_output.py b/src/openai/types/realtime/realtime_response_create_audio_output.py index 48a5d67e20..b8f4d284d5 100644 --- a/src/openai/types/realtime/realtime_response_create_audio_output.py +++ b/src/openai/types/realtime/realtime_response_create_audio_output.py @@ -26,4 +26,6 @@ class Output(BaseModel): class RealtimeResponseCreateAudioOutput(BaseModel): + """Configuration for audio input and output.""" + output: Optional[Output] = None diff --git a/src/openai/types/realtime/realtime_response_create_audio_output_param.py b/src/openai/types/realtime/realtime_response_create_audio_output_param.py index 9aa6d28835..30a4633698 100644 --- a/src/openai/types/realtime/realtime_response_create_audio_output_param.py +++ b/src/openai/types/realtime/realtime_response_create_audio_output_param.py @@ -25,4 +25,6 @@ class Output(TypedDict, total=False): class RealtimeResponseCreateAudioOutputParam(TypedDict, total=False): + """Configuration for audio input and output.""" + output: Output diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py index 119b4a455d..72189e10e6 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py @@ -17,6 +17,8 @@ class AllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -33,6 +35,8 @@ class AllowedToolsMcpToolFilter(BaseModel): class RequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -46,6 +50,8 @@ class RequireApprovalMcpToolApprovalFilterAlways(BaseModel): class RequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -59,6 +65,13 @@ class RequireApprovalMcpToolApprovalFilterNever(BaseModel): class RequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: Optional[RequireApprovalMcpToolApprovalFilterAlways] = None """A filter object to specify which tools are allowed.""" @@ -70,6 +83,11 @@ class RequireApprovalMcpToolApprovalFilter(BaseModel): class RealtimeResponseCreateMcpTool(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: str """A label for this MCP server, used to identify it in tool calls.""" diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py index 3b9cf047c1..68dd6bdb5c 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py @@ -19,6 +19,8 @@ class AllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -35,6 +37,8 @@ class AllowedToolsMcpToolFilter(TypedDict, total=False): class RequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -48,6 +52,8 @@ class RequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): class RequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -61,6 +67,13 @@ class RequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): class RequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: RequireApprovalMcpToolApprovalFilterAlways """A filter object to specify which tools are allowed.""" @@ -72,6 +85,11 @@ class RequireApprovalMcpToolApprovalFilter(TypedDict, total=False): class RealtimeResponseCreateMcpToolParam(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: Required[str] """A label for this MCP server, used to identify it in tool calls.""" diff --git a/src/openai/types/realtime/realtime_response_create_params.py b/src/openai/types/realtime/realtime_response_create_params.py index e8486220bf..deec8c9280 100644 --- a/src/openai/types/realtime/realtime_response_create_params.py +++ b/src/openai/types/realtime/realtime_response_create_params.py @@ -22,6 +22,8 @@ class RealtimeResponseCreateParams(BaseModel): + """Create a new Realtime response with these parameters""" + audio: Optional[RealtimeResponseCreateAudioOutput] = None """Configuration for audio input and output.""" diff --git a/src/openai/types/realtime/realtime_response_create_params_param.py b/src/openai/types/realtime/realtime_response_create_params_param.py index 116384bd82..caad5bc900 100644 --- a/src/openai/types/realtime/realtime_response_create_params_param.py +++ b/src/openai/types/realtime/realtime_response_create_params_param.py @@ -23,6 +23,8 @@ class RealtimeResponseCreateParamsParam(TypedDict, total=False): + """Create a new Realtime response with these parameters""" + audio: RealtimeResponseCreateAudioOutputParam """Configuration for audio input and output.""" diff --git a/src/openai/types/realtime/realtime_response_status.py b/src/openai/types/realtime/realtime_response_status.py index 12999f61a1..26b272ae5a 100644 --- a/src/openai/types/realtime/realtime_response_status.py +++ b/src/openai/types/realtime/realtime_response_status.py @@ -9,6 +9,11 @@ class Error(BaseModel): + """ + A description of the error that caused the response to fail, + populated when the `status` is `failed`. + """ + code: Optional[str] = None """Error code, if any.""" @@ -17,6 +22,8 @@ class Error(BaseModel): class RealtimeResponseStatus(BaseModel): + """Additional details about the status.""" + error: Optional[Error] = None """ A description of the error that caused the response to fail, populated when the diff --git a/src/openai/types/realtime/realtime_response_usage.py b/src/openai/types/realtime/realtime_response_usage.py index fb8893b346..a5985d8a7b 100644 --- a/src/openai/types/realtime/realtime_response_usage.py +++ b/src/openai/types/realtime/realtime_response_usage.py @@ -10,6 +10,14 @@ class RealtimeResponseUsage(BaseModel): + """Usage statistics for the Response, this will correspond to billing. + + A + Realtime API session will maintain a conversation context and append new + Items to the Conversation, thus output from previous turns (text and + audio tokens) will become the input for later turns. + """ + input_token_details: Optional[RealtimeResponseUsageInputTokenDetails] = None """Details about the input tokens used in the Response. diff --git a/src/openai/types/realtime/realtime_response_usage_input_token_details.py b/src/openai/types/realtime/realtime_response_usage_input_token_details.py index e14a74a84e..0fc71749e9 100644 --- a/src/openai/types/realtime/realtime_response_usage_input_token_details.py +++ b/src/openai/types/realtime/realtime_response_usage_input_token_details.py @@ -8,6 +8,8 @@ class CachedTokensDetails(BaseModel): + """Details about the cached tokens used as input for the Response.""" + audio_tokens: Optional[int] = None """The number of cached audio tokens used as input for the Response.""" @@ -19,6 +21,11 @@ class CachedTokensDetails(BaseModel): class RealtimeResponseUsageInputTokenDetails(BaseModel): + """Details about the input tokens used in the Response. + + Cached tokens are tokens from previous turns in the conversation that are included as context for the current response. Cached tokens here are counted as a subset of input tokens, meaning input tokens will include cached and uncached tokens. + """ + audio_tokens: Optional[int] = None """The number of audio tokens used as input for the Response.""" diff --git a/src/openai/types/realtime/realtime_response_usage_output_token_details.py b/src/openai/types/realtime/realtime_response_usage_output_token_details.py index dfa97a1f47..2154c77d5d 100644 --- a/src/openai/types/realtime/realtime_response_usage_output_token_details.py +++ b/src/openai/types/realtime/realtime_response_usage_output_token_details.py @@ -8,6 +8,8 @@ class RealtimeResponseUsageOutputTokenDetails(BaseModel): + """Details about the output tokens used in the Response.""" + audio_tokens: Optional[int] = None """The number of audio tokens used in the Response.""" diff --git a/src/openai/types/realtime/realtime_server_event.py b/src/openai/types/realtime/realtime_server_event.py index ead98f1a54..5de53d053e 100644 --- a/src/openai/types/realtime/realtime_server_event.py +++ b/src/openai/types/realtime/realtime_server_event.py @@ -61,6 +61,11 @@ class ConversationItemRetrieved(BaseModel): + """Returned when a conversation item is retrieved with `conversation.item.retrieve`. + + This is provided as a way to fetch the server's representation of an item, for example to get access to the post-processed audio data after noise cancellation and VAD. It includes the full content of the Item, including audio data. + """ + event_id: str """The unique ID of the server event.""" @@ -72,6 +77,13 @@ class ConversationItemRetrieved(BaseModel): class OutputAudioBufferStarted(BaseModel): + """ + **WebRTC/SIP Only:** Emitted when the server begins streaming audio to the client. This event is + emitted after an audio content part has been added (`response.content_part.added`) + to the response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + event_id: str """The unique ID of the server event.""" @@ -83,6 +95,13 @@ class OutputAudioBufferStarted(BaseModel): class OutputAudioBufferStopped(BaseModel): + """ + **WebRTC/SIP Only:** Emitted when the output audio buffer has been completely drained on the server, + and no more audio is forthcoming. This event is emitted after the full response + data has been sent to the client (`response.done`). + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + event_id: str """The unique ID of the server event.""" @@ -94,6 +113,15 @@ class OutputAudioBufferStopped(BaseModel): class OutputAudioBufferCleared(BaseModel): + """**WebRTC/SIP Only:** Emitted when the output audio buffer is cleared. + + This happens either in VAD + mode when the user has interrupted (`input_audio_buffer.speech_started`), + or when the client has emitted the `output_audio_buffer.clear` event to manually + cut off the current audio response. + [Learn more](https://platform.openai.com/docs/guides/realtime-conversations#client-and-server-events-for-audio-in-webrtc). + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/realtime_session_client_secret.py b/src/openai/types/realtime/realtime_session_client_secret.py index a4998802bb..13a12f5502 100644 --- a/src/openai/types/realtime/realtime_session_client_secret.py +++ b/src/openai/types/realtime/realtime_session_client_secret.py @@ -6,6 +6,8 @@ class RealtimeSessionClientSecret(BaseModel): + """Ephemeral key returned by the API.""" + expires_at: int """Timestamp for when the token expires. diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 80cf468dc8..76738816a0 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -15,6 +15,8 @@ class RealtimeSessionCreateRequest(BaseModel): + """Realtime session object configuration.""" + type: Literal["realtime"] """The type of session to create. Always `realtime` for the Realtime API.""" diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index 578d5a502d..cc5806fe11 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -16,6 +16,8 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): + """Realtime session object configuration.""" + type: Required[Literal["realtime"]] """The type of session to create. Always `realtime` for the Realtime API.""" diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index df69dd7bdb..46d32e8571 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -40,6 +40,13 @@ class AudioInputNoiseReduction(BaseModel): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + type: Optional[NoiseReductionType] = None """Type of noise reduction. @@ -49,6 +56,10 @@ class AudioInputNoiseReduction(BaseModel): class AudioInputTurnDetectionServerVad(BaseModel): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + type: Literal["server_vad"] """Type of turn detection, `server_vad` to turn on simple Server VAD.""" @@ -115,6 +126,10 @@ class AudioInputTurnDetectionServerVad(BaseModel): class AudioInputTurnDetectionSemanticVad(BaseModel): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + type: Literal["semantic_vad"] """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" @@ -218,6 +233,8 @@ class AudioOutput(BaseModel): class Audio(BaseModel): + """Configuration for input and output audio.""" + input: Optional[AudioInput] = None output: Optional[AudioOutput] = None @@ -227,6 +244,8 @@ class Audio(BaseModel): class ToolMcpToolAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -243,6 +262,8 @@ class ToolMcpToolAllowedToolsMcpToolFilter(BaseModel): class ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -256,6 +277,8 @@ class ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways(BaseModel): class ToolMcpToolRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -269,6 +292,13 @@ class ToolMcpToolRequireApprovalMcpToolApprovalFilterNever(BaseModel): class ToolMcpToolRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: Optional[ToolMcpToolRequireApprovalMcpToolApprovalFilterAlways] = None """A filter object to specify which tools are allowed.""" @@ -282,6 +312,11 @@ class ToolMcpToolRequireApprovalMcpToolApprovalFilter(BaseModel): class ToolMcpTool(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: str """A label for this MCP server, used to identify it in tool calls.""" @@ -351,6 +386,8 @@ class ToolMcpTool(BaseModel): class TracingTracingConfiguration(BaseModel): + """Granular configuration for tracing.""" + group_id: Optional[str] = None """ The group id to attach to this trace to enable filtering and grouping in the @@ -374,6 +411,12 @@ class TracingTracingConfiguration(BaseModel): class RealtimeSessionCreateResponse(BaseModel): + """A new Realtime session configuration, with an ephemeral key. + + Default TTL + for keys is one minute. + """ + client_secret: RealtimeSessionClientSecret """Ephemeral key returned by the API.""" diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py index 630fc74691..3cc404feef 100644 --- a/src/openai/types/realtime/realtime_tools_config_param.py +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -22,6 +22,8 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -38,6 +40,8 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -51,6 +55,8 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -64,6 +70,13 @@ class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: McpRequireApprovalMcpToolApprovalFilterAlways """A filter object to specify which tools are allowed.""" @@ -75,6 +88,11 @@ class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: Required[str] """A label for this MCP server, used to identify it in tool calls.""" diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py index e7126ed60d..92aaee7f26 100644 --- a/src/openai/types/realtime/realtime_tools_config_union.py +++ b/src/openai/types/realtime/realtime_tools_config_union.py @@ -20,6 +20,8 @@ class McpAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -36,6 +38,8 @@ class McpAllowedToolsMcpToolFilter(BaseModel): class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -49,6 +53,8 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -62,6 +68,13 @@ class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): class McpRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None """A filter object to specify which tools are allowed.""" @@ -73,6 +86,11 @@ class McpRequireApprovalMcpToolApprovalFilter(BaseModel): class Mcp(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: str """A label for this MCP server, used to identify it in tool calls.""" diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py index 9ee58fdbe6..6889b4c304 100644 --- a/src/openai/types/realtime/realtime_tools_config_union_param.py +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -21,6 +21,8 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -37,6 +39,8 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -50,6 +54,8 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -63,6 +69,13 @@ class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: McpRequireApprovalMcpToolApprovalFilterAlways """A filter object to specify which tools are allowed.""" @@ -74,6 +87,11 @@ class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: Required[str] """A label for this MCP server, used to identify it in tool calls.""" diff --git a/src/openai/types/realtime/realtime_tracing_config.py b/src/openai/types/realtime/realtime_tracing_config.py index 1c46de7928..37e3ce8945 100644 --- a/src/openai/types/realtime/realtime_tracing_config.py +++ b/src/openai/types/realtime/realtime_tracing_config.py @@ -9,6 +9,8 @@ class TracingConfiguration(BaseModel): + """Granular configuration for tracing.""" + group_id: Optional[str] = None """ The group id to attach to this trace to enable filtering and grouping in the diff --git a/src/openai/types/realtime/realtime_tracing_config_param.py b/src/openai/types/realtime/realtime_tracing_config_param.py index fd9e266244..742412897f 100644 --- a/src/openai/types/realtime/realtime_tracing_config_param.py +++ b/src/openai/types/realtime/realtime_tracing_config_param.py @@ -9,6 +9,8 @@ class TracingConfiguration(TypedDict, total=False): + """Granular configuration for tracing.""" + group_id: str """ The group id to attach to this trace to enable filtering and grouping in the diff --git a/src/openai/types/realtime/realtime_transcription_session_audio.py b/src/openai/types/realtime/realtime_transcription_session_audio.py index a5506947f1..7ec29afb79 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio.py @@ -9,4 +9,6 @@ class RealtimeTranscriptionSessionAudio(BaseModel): + """Configuration for input and output audio.""" + input: Optional[RealtimeTranscriptionSessionAudioInput] = None diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input.py b/src/openai/types/realtime/realtime_transcription_session_audio_input.py index efc321cbeb..80ff223590 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input.py @@ -14,6 +14,13 @@ class NoiseReduction(BaseModel): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + type: Optional[NoiseReductionType] = None """Type of noise reduction. diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py index c9153b68a4..dd908c72f6 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py @@ -16,6 +16,13 @@ class NoiseReduction(TypedDict, total=False): + """Configuration for input audio noise reduction. + + This can be set to `null` to turn off. + Noise reduction filters audio added to the input audio buffer before it is sent to VAD and the model. + Filtering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio. + """ + type: NoiseReductionType """Type of noise reduction. diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py index e21844f48f..3d4ee779f4 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection.py @@ -10,6 +10,10 @@ class ServerVad(BaseModel): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + type: Literal["server_vad"] """Type of turn detection, `server_vad` to turn on simple Server VAD.""" @@ -76,6 +80,10 @@ class ServerVad(BaseModel): class SemanticVad(BaseModel): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + type: Literal["semantic_vad"] """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py index 507c43141e..0aca59ce11 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_turn_detection_param.py @@ -9,6 +9,10 @@ class ServerVad(TypedDict, total=False): + """ + Server-side voice activity detection (VAD) which flips on when user speech is detected and off after a period of silence. + """ + type: Required[Literal["server_vad"]] """Type of turn detection, `server_vad` to turn on simple Server VAD.""" @@ -75,6 +79,10 @@ class ServerVad(TypedDict, total=False): class SemanticVad(TypedDict, total=False): + """ + Server-side semantic turn detection which uses a model to determine when the user has finished speaking. + """ + type: Required[Literal["semantic_vad"]] """Type of turn detection, `semantic_vad` to turn on Semantic VAD.""" diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_param.py index 1503a606d3..6bf1117917 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_param.py @@ -10,4 +10,6 @@ class RealtimeTranscriptionSessionAudioParam(TypedDict, total=False): + """Configuration for input and output audio.""" + input: RealtimeTranscriptionSessionAudioInputParam diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request.py b/src/openai/types/realtime/realtime_transcription_session_create_request.py index 102f2b14fb..f72a4ad93f 100644 --- a/src/openai/types/realtime/realtime_transcription_session_create_request.py +++ b/src/openai/types/realtime/realtime_transcription_session_create_request.py @@ -10,6 +10,8 @@ class RealtimeTranscriptionSessionCreateRequest(BaseModel): + """Realtime transcription session object configuration.""" + type: Literal["transcription"] """The type of session to create. diff --git a/src/openai/types/realtime/realtime_transcription_session_create_request_param.py b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py index 80cbe2d414..9b4d8ead79 100644 --- a/src/openai/types/realtime/realtime_transcription_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_create_request_param.py @@ -11,6 +11,8 @@ class RealtimeTranscriptionSessionCreateRequestParam(TypedDict, total=False): + """Realtime transcription session object configuration.""" + type: Required[Literal["transcription"]] """The type of session to create. diff --git a/src/openai/types/realtime/realtime_transcription_session_create_response.py b/src/openai/types/realtime/realtime_transcription_session_create_response.py index 301af1ac3f..6ca6c3808b 100644 --- a/src/openai/types/realtime/realtime_transcription_session_create_response.py +++ b/src/openai/types/realtime/realtime_transcription_session_create_response.py @@ -13,6 +13,8 @@ class AudioInputNoiseReduction(BaseModel): + """Configuration for input audio noise reduction.""" + type: Optional[NoiseReductionType] = None """Type of noise reduction. @@ -41,10 +43,14 @@ class AudioInput(BaseModel): class Audio(BaseModel): + """Configuration for input audio for the session.""" + input: Optional[AudioInput] = None class RealtimeTranscriptionSessionCreateResponse(BaseModel): + """A Realtime transcription session configuration object.""" + id: str """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" diff --git a/src/openai/types/realtime/realtime_transcription_session_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py index f5da31ce77..8dacd60a07 100644 --- a/src/openai/types/realtime/realtime_transcription_session_turn_detection.py +++ b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py @@ -8,6 +8,13 @@ class RealtimeTranscriptionSessionTurnDetection(BaseModel): + """Configuration for turn detection. + + Can be set to `null` to turn off. Server + VAD means that the model will detect the start and end of speech based on + audio volume and respond at the end of user speech. + """ + prefix_padding_ms: Optional[int] = None """Amount of audio to include before the VAD detected speech (in milliseconds). diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio.py b/src/openai/types/realtime/realtime_truncation_retention_ratio.py index e19ed64831..72a93a5654 100644 --- a/src/openai/types/realtime/realtime_truncation_retention_ratio.py +++ b/src/openai/types/realtime/realtime_truncation_retention_ratio.py @@ -9,6 +9,11 @@ class TokenLimits(BaseModel): + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ + post_instructions: Optional[int] = None """ Maximum tokens allowed in the conversation after instructions (which including @@ -20,6 +25,10 @@ class TokenLimits(BaseModel): class RealtimeTruncationRetentionRatio(BaseModel): + """ + Retain a fraction of the conversation tokens when the conversation exceeds the input token limit. This allows you to amortize truncations across multiple turns, which can help improve cached token usage. + """ + retention_ratio: float """ Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when diff --git a/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py index 4ea80fe4ce..4648fa66b0 100644 --- a/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py +++ b/src/openai/types/realtime/realtime_truncation_retention_ratio_param.py @@ -8,6 +8,11 @@ class TokenLimits(TypedDict, total=False): + """Optional custom token limits for this truncation strategy. + + If not provided, the model's default token limits will be used. + """ + post_instructions: int """ Maximum tokens allowed in the conversation after instructions (which including @@ -19,6 +24,10 @@ class TokenLimits(TypedDict, total=False): class RealtimeTruncationRetentionRatioParam(TypedDict, total=False): + """ + Retain a fraction of the conversation tokens when the conversation exceeds the input token limit. This allows you to amortize truncations across multiple turns, which can help improve cached token usage. + """ + retention_ratio: Required[float] """ Fraction of post-instruction conversation tokens to retain (`0.0` - `1.0`) when diff --git a/src/openai/types/realtime/response_audio_delta_event.py b/src/openai/types/realtime/response_audio_delta_event.py index d92c5462d0..ae87014053 100644 --- a/src/openai/types/realtime/response_audio_delta_event.py +++ b/src/openai/types/realtime/response_audio_delta_event.py @@ -8,6 +8,8 @@ class ResponseAudioDeltaEvent(BaseModel): + """Returned when the model-generated audio is updated.""" + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/response_audio_done_event.py b/src/openai/types/realtime/response_audio_done_event.py index 5ea0f07e36..98715aba13 100644 --- a/src/openai/types/realtime/response_audio_done_event.py +++ b/src/openai/types/realtime/response_audio_done_event.py @@ -8,6 +8,12 @@ class ResponseAudioDoneEvent(BaseModel): + """Returned when the model-generated audio is done. + + Also emitted when a Response + is interrupted, incomplete, or cancelled. + """ + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/response_audio_transcript_delta_event.py b/src/openai/types/realtime/response_audio_transcript_delta_event.py index 4dd5fecac0..4ec1a820ba 100644 --- a/src/openai/types/realtime/response_audio_transcript_delta_event.py +++ b/src/openai/types/realtime/response_audio_transcript_delta_event.py @@ -8,6 +8,8 @@ class ResponseAudioTranscriptDeltaEvent(BaseModel): + """Returned when the model-generated transcription of audio output is updated.""" + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/response_audio_transcript_done_event.py b/src/openai/types/realtime/response_audio_transcript_done_event.py index 2de913d277..c2a2416355 100644 --- a/src/openai/types/realtime/response_audio_transcript_done_event.py +++ b/src/openai/types/realtime/response_audio_transcript_done_event.py @@ -8,6 +8,12 @@ class ResponseAudioTranscriptDoneEvent(BaseModel): + """ + Returned when the model-generated transcription of audio output is done + streaming. Also emitted when a Response is interrupted, incomplete, or + cancelled. + """ + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/response_cancel_event.py b/src/openai/types/realtime/response_cancel_event.py index 15dc141cbf..9c6113998f 100644 --- a/src/openai/types/realtime/response_cancel_event.py +++ b/src/openai/types/realtime/response_cancel_event.py @@ -9,6 +9,15 @@ class ResponseCancelEvent(BaseModel): + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. + """ + type: Literal["response.cancel"] """The event type, must be `response.cancel`.""" diff --git a/src/openai/types/realtime/response_cancel_event_param.py b/src/openai/types/realtime/response_cancel_event_param.py index f33740730a..b233b407f9 100644 --- a/src/openai/types/realtime/response_cancel_event_param.py +++ b/src/openai/types/realtime/response_cancel_event_param.py @@ -8,6 +8,15 @@ class ResponseCancelEventParam(TypedDict, total=False): + """Send this event to cancel an in-progress response. + + The server will respond + with a `response.done` event with a status of `response.status=cancelled`. If + there is no response to cancel, the server will respond with an error. It's safe + to call `response.cancel` even if no response is in progress, an error will be + returned the session will remain unaffected. + """ + type: Required[Literal["response.cancel"]] """The event type, must be `response.cancel`.""" diff --git a/src/openai/types/realtime/response_content_part_added_event.py b/src/openai/types/realtime/response_content_part_added_event.py index aca965c3d8..e47c84af20 100644 --- a/src/openai/types/realtime/response_content_part_added_event.py +++ b/src/openai/types/realtime/response_content_part_added_event.py @@ -9,6 +9,8 @@ class Part(BaseModel): + """The content part that was added.""" + audio: Optional[str] = None """Base64-encoded audio data (if type is "audio").""" @@ -23,6 +25,11 @@ class Part(BaseModel): class ResponseContentPartAddedEvent(BaseModel): + """ + Returned when a new content part is added to an assistant message item during + response generation. + """ + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/response_content_part_done_event.py b/src/openai/types/realtime/response_content_part_done_event.py index 59af808a90..a6cb8559b9 100644 --- a/src/openai/types/realtime/response_content_part_done_event.py +++ b/src/openai/types/realtime/response_content_part_done_event.py @@ -9,6 +9,8 @@ class Part(BaseModel): + """The content part that is done.""" + audio: Optional[str] = None """Base64-encoded audio data (if type is "audio").""" @@ -23,6 +25,11 @@ class Part(BaseModel): class ResponseContentPartDoneEvent(BaseModel): + """ + Returned when a content part is done streaming in an assistant message item. + Also emitted when a Response is interrupted, incomplete, or cancelled. + """ + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/response_create_event.py b/src/openai/types/realtime/response_create_event.py index 75a08ee460..3e98a8d858 100644 --- a/src/openai/types/realtime/response_create_event.py +++ b/src/openai/types/realtime/response_create_event.py @@ -10,6 +10,34 @@ class ResponseCreateEvent(BaseModel): + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history by default. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions` and `tools`. If these are set, they will override the Session's + configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. + """ + type: Literal["response.create"] """The event type, must be `response.create`.""" diff --git a/src/openai/types/realtime/response_create_event_param.py b/src/openai/types/realtime/response_create_event_param.py index e5dd46d9b6..9da89e14ee 100644 --- a/src/openai/types/realtime/response_create_event_param.py +++ b/src/openai/types/realtime/response_create_event_param.py @@ -10,6 +10,34 @@ class ResponseCreateEventParam(TypedDict, total=False): + """ + This event instructs the server to create a Response, which means triggering + model inference. When in Server VAD mode, the server will create Responses + automatically. + + A Response will include at least one Item, and may have two, in which case + the second will be a function call. These Items will be appended to the + conversation history by default. + + The server will respond with a `response.created` event, events for Items + and content created, and finally a `response.done` event to indicate the + Response is complete. + + The `response.create` event includes inference configuration like + `instructions` and `tools`. If these are set, they will override the Session's + configuration for this Response only. + + Responses can be created out-of-band of the default Conversation, meaning that they can + have arbitrary input, and it's possible to disable writing the output to the Conversation. + Only one Response can write to the default Conversation at a time, but otherwise multiple + Responses can be created in parallel. The `metadata` field is a good way to disambiguate + multiple simultaneous Responses. + + Clients can set `conversation` to `none` to create a Response that does not write to the default + Conversation. Arbitrary input can be provided with the `input` field, which is an array accepting + raw Items and references to existing Items. + """ + type: Required[Literal["response.create"]] """The event type, must be `response.create`.""" diff --git a/src/openai/types/realtime/response_created_event.py b/src/openai/types/realtime/response_created_event.py index 996bf26f75..dc5941262d 100644 --- a/src/openai/types/realtime/response_created_event.py +++ b/src/openai/types/realtime/response_created_event.py @@ -9,6 +9,12 @@ class ResponseCreatedEvent(BaseModel): + """Returned when a new Response is created. + + The first event of response creation, + where the response is in an initial state of `in_progress`. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/response_done_event.py b/src/openai/types/realtime/response_done_event.py index ce9a4b9f1d..9c31a2aa03 100644 --- a/src/openai/types/realtime/response_done_event.py +++ b/src/openai/types/realtime/response_done_event.py @@ -9,6 +9,19 @@ class ResponseDoneEvent(BaseModel): + """Returned when a Response is done streaming. + + Always emitted, no matter the + final state. The Response object included in the `response.done` event will + include all output Items in the Response but will omit the raw audio data. + + Clients should check the `status` field of the Response to determine if it was successful + (`completed`) or if there was another outcome: `cancelled`, `failed`, or `incomplete`. + + A response will contain all output items that were generated during the response, excluding + any audio content. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/response_function_call_arguments_delta_event.py b/src/openai/types/realtime/response_function_call_arguments_delta_event.py index 6d96e78b24..a426c3f211 100644 --- a/src/openai/types/realtime/response_function_call_arguments_delta_event.py +++ b/src/openai/types/realtime/response_function_call_arguments_delta_event.py @@ -8,6 +8,8 @@ class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + """Returned when the model-generated function call arguments are updated.""" + call_id: str """The ID of the function call.""" diff --git a/src/openai/types/realtime/response_function_call_arguments_done_event.py b/src/openai/types/realtime/response_function_call_arguments_done_event.py index be7fae9a1b..504f91d558 100644 --- a/src/openai/types/realtime/response_function_call_arguments_done_event.py +++ b/src/openai/types/realtime/response_function_call_arguments_done_event.py @@ -8,6 +8,11 @@ class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + """ + Returned when the model-generated function call arguments are done streaming. + Also emitted when a Response is interrupted, incomplete, or cancelled. + """ + arguments: str """The final arguments as a JSON string.""" diff --git a/src/openai/types/realtime/response_mcp_call_arguments_delta.py b/src/openai/types/realtime/response_mcp_call_arguments_delta.py index 0a02a1a578..d890de0575 100644 --- a/src/openai/types/realtime/response_mcp_call_arguments_delta.py +++ b/src/openai/types/realtime/response_mcp_call_arguments_delta.py @@ -9,6 +9,8 @@ class ResponseMcpCallArgumentsDelta(BaseModel): + """Returned when MCP tool call arguments are updated during response generation.""" + delta: str """The JSON-encoded arguments delta.""" diff --git a/src/openai/types/realtime/response_mcp_call_arguments_done.py b/src/openai/types/realtime/response_mcp_call_arguments_done.py index 5ec95f1728..a7cb2d1958 100644 --- a/src/openai/types/realtime/response_mcp_call_arguments_done.py +++ b/src/openai/types/realtime/response_mcp_call_arguments_done.py @@ -8,6 +8,8 @@ class ResponseMcpCallArgumentsDone(BaseModel): + """Returned when MCP tool call arguments are finalized during response generation.""" + arguments: str """The final JSON-encoded arguments string.""" diff --git a/src/openai/types/realtime/response_mcp_call_completed.py b/src/openai/types/realtime/response_mcp_call_completed.py index e3fcec21f0..130260539a 100644 --- a/src/openai/types/realtime/response_mcp_call_completed.py +++ b/src/openai/types/realtime/response_mcp_call_completed.py @@ -8,6 +8,8 @@ class ResponseMcpCallCompleted(BaseModel): + """Returned when an MCP tool call has completed successfully.""" + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/response_mcp_call_failed.py b/src/openai/types/realtime/response_mcp_call_failed.py index b7adc8c2a7..1c08d1d4b7 100644 --- a/src/openai/types/realtime/response_mcp_call_failed.py +++ b/src/openai/types/realtime/response_mcp_call_failed.py @@ -8,6 +8,8 @@ class ResponseMcpCallFailed(BaseModel): + """Returned when an MCP tool call has failed.""" + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/response_mcp_call_in_progress.py b/src/openai/types/realtime/response_mcp_call_in_progress.py index d0fcc7615c..4c0ad149e5 100644 --- a/src/openai/types/realtime/response_mcp_call_in_progress.py +++ b/src/openai/types/realtime/response_mcp_call_in_progress.py @@ -8,6 +8,8 @@ class ResponseMcpCallInProgress(BaseModel): + """Returned when an MCP tool call has started and is in progress.""" + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/response_output_item_added_event.py b/src/openai/types/realtime/response_output_item_added_event.py index 509dfcaeaf..abec0d18f1 100644 --- a/src/openai/types/realtime/response_output_item_added_event.py +++ b/src/openai/types/realtime/response_output_item_added_event.py @@ -9,6 +9,8 @@ class ResponseOutputItemAddedEvent(BaseModel): + """Returned when a new Item is created during Response generation.""" + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/response_output_item_done_event.py b/src/openai/types/realtime/response_output_item_done_event.py index 800e4ae8ee..63936b97d5 100644 --- a/src/openai/types/realtime/response_output_item_done_event.py +++ b/src/openai/types/realtime/response_output_item_done_event.py @@ -9,6 +9,12 @@ class ResponseOutputItemDoneEvent(BaseModel): + """Returned when an Item is done streaming. + + Also emitted when a Response is + interrupted, incomplete, or cancelled. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/response_text_delta_event.py b/src/openai/types/realtime/response_text_delta_event.py index 493348aa22..b251b7639c 100644 --- a/src/openai/types/realtime/response_text_delta_event.py +++ b/src/openai/types/realtime/response_text_delta_event.py @@ -8,6 +8,8 @@ class ResponseTextDeltaEvent(BaseModel): + """Returned when the text value of an "output_text" content part is updated.""" + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/response_text_done_event.py b/src/openai/types/realtime/response_text_done_event.py index 83c6cf0694..046e520222 100644 --- a/src/openai/types/realtime/response_text_done_event.py +++ b/src/openai/types/realtime/response_text_done_event.py @@ -8,6 +8,12 @@ class ResponseTextDoneEvent(BaseModel): + """Returned when the text value of an "output_text" content part is done streaming. + + Also + emitted when a Response is interrupted, incomplete, or cancelled. + """ + content_index: int """The index of the content part in the item's content array.""" diff --git a/src/openai/types/realtime/session_created_event.py b/src/openai/types/realtime/session_created_event.py index b5caad35d7..1b8d4a4d81 100644 --- a/src/openai/types/realtime/session_created_event.py +++ b/src/openai/types/realtime/session_created_event.py @@ -13,6 +13,13 @@ class SessionCreatedEvent(BaseModel): + """Returned when a Session is created. + + Emitted automatically when a new + connection is established as the first server event. This event will contain + the default Session configuration. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/realtime/session_update_event.py b/src/openai/types/realtime/session_update_event.py index 2e226162c4..a8422e4e89 100644 --- a/src/openai/types/realtime/session_update_event.py +++ b/src/openai/types/realtime/session_update_event.py @@ -13,6 +13,18 @@ class SessionUpdateEvent(BaseModel): + """ + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. + """ + session: Session """Update the Realtime session. diff --git a/src/openai/types/realtime/session_update_event_param.py b/src/openai/types/realtime/session_update_event_param.py index 5962361431..910e89ca34 100644 --- a/src/openai/types/realtime/session_update_event_param.py +++ b/src/openai/types/realtime/session_update_event_param.py @@ -14,6 +14,18 @@ class SessionUpdateEventParam(TypedDict, total=False): + """ + Send this event to update the session’s configuration. + The client may send this event at any time to update any field + except for `voice` and `model`. `voice` can be updated only if there have been no other audio outputs yet. + + When the server receives a `session.update`, it will respond + with a `session.updated` event showing the full, effective configuration. + Only the fields that are present in the `session.update` are updated. To clear a field like + `instructions`, pass an empty string. To clear a field like `tools`, pass an empty array. + To clear a field like `turn_detection`, pass `null`. + """ + session: Required[Session] """Update the Realtime session. diff --git a/src/openai/types/realtime/session_updated_event.py b/src/openai/types/realtime/session_updated_event.py index eb7ee0332d..e68a08d6cc 100644 --- a/src/openai/types/realtime/session_updated_event.py +++ b/src/openai/types/realtime/session_updated_event.py @@ -13,6 +13,11 @@ class SessionUpdatedEvent(BaseModel): + """ + Returned when a session is updated with a `session.update` event, unless + there is an error. + """ + event_id: str """The unique ID of the server event.""" diff --git a/src/openai/types/responses/apply_patch_tool.py b/src/openai/types/responses/apply_patch_tool.py index 07706ce239..f2ed245d10 100644 --- a/src/openai/types/responses/apply_patch_tool.py +++ b/src/openai/types/responses/apply_patch_tool.py @@ -8,5 +8,7 @@ class ApplyPatchTool(BaseModel): + """Allows the assistant to create, delete, or update files using unified diffs.""" + type: Literal["apply_patch"] """The type of the tool. Always `apply_patch`.""" diff --git a/src/openai/types/responses/apply_patch_tool_param.py b/src/openai/types/responses/apply_patch_tool_param.py index 93d15f0b1f..2e0a809099 100644 --- a/src/openai/types/responses/apply_patch_tool_param.py +++ b/src/openai/types/responses/apply_patch_tool_param.py @@ -8,5 +8,7 @@ class ApplyPatchToolParam(TypedDict, total=False): + """Allows the assistant to create, delete, or update files using unified diffs.""" + type: Required[Literal["apply_patch"]] """The type of the tool. Always `apply_patch`.""" diff --git a/src/openai/types/responses/computer_tool.py b/src/openai/types/responses/computer_tool.py index 5b844f5bf4..22871c841c 100644 --- a/src/openai/types/responses/computer_tool.py +++ b/src/openai/types/responses/computer_tool.py @@ -8,6 +8,11 @@ class ComputerTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + display_height: int """The height of the computer display.""" diff --git a/src/openai/types/responses/computer_tool_param.py b/src/openai/types/responses/computer_tool_param.py index 06a5c132ec..cdf75a43f2 100644 --- a/src/openai/types/responses/computer_tool_param.py +++ b/src/openai/types/responses/computer_tool_param.py @@ -8,6 +8,11 @@ class ComputerToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + display_height: Required[int] """The height of the computer display.""" diff --git a/src/openai/types/responses/custom_tool.py b/src/openai/types/responses/custom_tool.py index c16ae715eb..1ca401a486 100644 --- a/src/openai/types/responses/custom_tool.py +++ b/src/openai/types/responses/custom_tool.py @@ -10,6 +10,11 @@ class CustomTool(BaseModel): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + """ + name: str """The name of the custom tool, used to identify it in tool calls.""" diff --git a/src/openai/types/responses/custom_tool_param.py b/src/openai/types/responses/custom_tool_param.py index 2afc8b19b8..4ce43cdfdb 100644 --- a/src/openai/types/responses/custom_tool_param.py +++ b/src/openai/types/responses/custom_tool_param.py @@ -10,6 +10,11 @@ class CustomToolParam(TypedDict, total=False): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + """ + name: Required[str] """The name of the custom tool, used to identify it in tool calls.""" diff --git a/src/openai/types/responses/easy_input_message.py b/src/openai/types/responses/easy_input_message.py index 4ed0194f9f..9a36a6b084 100644 --- a/src/openai/types/responses/easy_input_message.py +++ b/src/openai/types/responses/easy_input_message.py @@ -10,6 +10,14 @@ class EasyInputMessage(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: Union[str, ResponseInputMessageContentList] """ Text, image, or audio input to the model, used to generate a response. Can also diff --git a/src/openai/types/responses/easy_input_message_param.py b/src/openai/types/responses/easy_input_message_param.py index ef2f1c5f37..0a382bddee 100644 --- a/src/openai/types/responses/easy_input_message_param.py +++ b/src/openai/types/responses/easy_input_message_param.py @@ -11,6 +11,14 @@ class EasyInputMessageParam(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + content: Required[Union[str, ResponseInputMessageContentListParam]] """ Text, image, or audio input to the model, used to generate a response. Can also diff --git a/src/openai/types/responses/file_search_tool.py b/src/openai/types/responses/file_search_tool.py index d0d08a323f..09c12876ca 100644 --- a/src/openai/types/responses/file_search_tool.py +++ b/src/openai/types/responses/file_search_tool.py @@ -13,6 +13,10 @@ class RankingOptionsHybridSearch(BaseModel): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + embedding_weight: float """The weight of the embedding in the reciprocal ranking fusion.""" @@ -21,6 +25,8 @@ class RankingOptionsHybridSearch(BaseModel): class RankingOptions(BaseModel): + """Ranking options for search.""" + hybrid_search: Optional[RankingOptionsHybridSearch] = None """ Weights that control how reciprocal rank fusion balances semantic embedding @@ -39,6 +45,11 @@ class RankingOptions(BaseModel): class FileSearchTool(BaseModel): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search). + """ + type: Literal["file_search"] """The type of the file search tool. Always `file_search`.""" diff --git a/src/openai/types/responses/file_search_tool_param.py b/src/openai/types/responses/file_search_tool_param.py index b37a669ebd..82831d0dc0 100644 --- a/src/openai/types/responses/file_search_tool_param.py +++ b/src/openai/types/responses/file_search_tool_param.py @@ -15,6 +15,10 @@ class RankingOptionsHybridSearch(TypedDict, total=False): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + embedding_weight: Required[float] """The weight of the embedding in the reciprocal ranking fusion.""" @@ -23,6 +27,8 @@ class RankingOptionsHybridSearch(TypedDict, total=False): class RankingOptions(TypedDict, total=False): + """Ranking options for search.""" + hybrid_search: RankingOptionsHybridSearch """ Weights that control how reciprocal rank fusion balances semantic embedding @@ -41,6 +47,11 @@ class RankingOptions(TypedDict, total=False): class FileSearchToolParam(TypedDict, total=False): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search). + """ + type: Required[Literal["file_search"]] """The type of the file search tool. Always `file_search`.""" diff --git a/src/openai/types/responses/function_shell_tool.py b/src/openai/types/responses/function_shell_tool.py index 1784b6c2f1..5b237aa705 100644 --- a/src/openai/types/responses/function_shell_tool.py +++ b/src/openai/types/responses/function_shell_tool.py @@ -8,5 +8,7 @@ class FunctionShellTool(BaseModel): + """A tool that allows the model to execute shell commands.""" + type: Literal["shell"] """The type of the shell tool. Always `shell`.""" diff --git a/src/openai/types/responses/function_shell_tool_param.py b/src/openai/types/responses/function_shell_tool_param.py index cee7ba23c9..c640ddab99 100644 --- a/src/openai/types/responses/function_shell_tool_param.py +++ b/src/openai/types/responses/function_shell_tool_param.py @@ -8,5 +8,7 @@ class FunctionShellToolParam(TypedDict, total=False): + """A tool that allows the model to execute shell commands.""" + type: Required[Literal["shell"]] """The type of the shell tool. Always `shell`.""" diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py index d881565356..b0827a9fa7 100644 --- a/src/openai/types/responses/function_tool.py +++ b/src/openai/types/responses/function_tool.py @@ -9,6 +9,11 @@ class FunctionTool(BaseModel): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + """ + name: str """The name of the function to call.""" diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py index 56bab36f47..ba0a3168c4 100644 --- a/src/openai/types/responses/function_tool_param.py +++ b/src/openai/types/responses/function_tool_param.py @@ -9,6 +9,11 @@ class FunctionToolParam(TypedDict, total=False): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + """ + name: Required[str] """The name of the function to call.""" diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py index 296d0718d8..50cc950e41 100644 --- a/src/openai/types/responses/input_token_count_params.py +++ b/src/openai/types/responses/input_token_count_params.py @@ -105,6 +105,14 @@ class InputTokenCountParams(TypedDict, total=False): class Text(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: ResponseFormatTextConfigParam """An object specifying the format that the model must output. diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index cdd143f1cb..00c38c064e 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -28,6 +28,8 @@ class IncompleteDetails(BaseModel): + """Details about why the response is incomplete.""" + reason: Optional[Literal["max_output_tokens", "content_filter"]] = None """The reason why the response is incomplete.""" @@ -45,6 +47,11 @@ class IncompleteDetails(BaseModel): class Conversation(BaseModel): + """The conversation that this response belongs to. + + Input items and output items from this response are automatically added to this conversation. + """ + id: str """The unique ID of the conversation.""" diff --git a/src/openai/types/responses/response_apply_patch_tool_call.py b/src/openai/types/responses/response_apply_patch_tool_call.py index 7dc2a3c2b5..7af1300265 100644 --- a/src/openai/types/responses/response_apply_patch_tool_call.py +++ b/src/openai/types/responses/response_apply_patch_tool_call.py @@ -16,6 +16,8 @@ class OperationCreateFile(BaseModel): + """Instruction describing how to create a file via the apply_patch tool.""" + diff: str """Diff to apply.""" @@ -27,6 +29,8 @@ class OperationCreateFile(BaseModel): class OperationDeleteFile(BaseModel): + """Instruction describing how to delete a file via the apply_patch tool.""" + path: str """Path of the file to delete.""" @@ -35,6 +39,8 @@ class OperationDeleteFile(BaseModel): class OperationUpdateFile(BaseModel): + """Instruction describing how to update a file via the apply_patch tool.""" + diff: str """Diff to apply.""" @@ -51,6 +57,8 @@ class OperationUpdateFile(BaseModel): class ResponseApplyPatchToolCall(BaseModel): + """A tool call that applies file diffs by creating, deleting, or updating files.""" + id: str """The unique ID of the apply patch tool call. diff --git a/src/openai/types/responses/response_apply_patch_tool_call_output.py b/src/openai/types/responses/response_apply_patch_tool_call_output.py index cf0bcfeecc..de63c6e2ee 100644 --- a/src/openai/types/responses/response_apply_patch_tool_call_output.py +++ b/src/openai/types/responses/response_apply_patch_tool_call_output.py @@ -9,6 +9,8 @@ class ResponseApplyPatchToolCallOutput(BaseModel): + """The output emitted by an apply patch tool call.""" + id: str """The unique ID of the apply patch tool call output. diff --git a/src/openai/types/responses/response_audio_delta_event.py b/src/openai/types/responses/response_audio_delta_event.py index 6fb7887b80..e577d65d04 100644 --- a/src/openai/types/responses/response_audio_delta_event.py +++ b/src/openai/types/responses/response_audio_delta_event.py @@ -8,6 +8,8 @@ class ResponseAudioDeltaEvent(BaseModel): + """Emitted when there is a partial audio response.""" + delta: str """A chunk of Base64 encoded response audio bytes.""" diff --git a/src/openai/types/responses/response_audio_done_event.py b/src/openai/types/responses/response_audio_done_event.py index 2592ae8dcd..f5f0401c86 100644 --- a/src/openai/types/responses/response_audio_done_event.py +++ b/src/openai/types/responses/response_audio_done_event.py @@ -8,6 +8,8 @@ class ResponseAudioDoneEvent(BaseModel): + """Emitted when the audio response is complete.""" + sequence_number: int """The sequence number of the delta.""" diff --git a/src/openai/types/responses/response_audio_transcript_delta_event.py b/src/openai/types/responses/response_audio_transcript_delta_event.py index 830c133d61..03be59a29f 100644 --- a/src/openai/types/responses/response_audio_transcript_delta_event.py +++ b/src/openai/types/responses/response_audio_transcript_delta_event.py @@ -8,6 +8,8 @@ class ResponseAudioTranscriptDeltaEvent(BaseModel): + """Emitted when there is a partial transcript of audio.""" + delta: str """The partial transcript of the audio response.""" diff --git a/src/openai/types/responses/response_audio_transcript_done_event.py b/src/openai/types/responses/response_audio_transcript_done_event.py index e39f501cf0..87219e4844 100644 --- a/src/openai/types/responses/response_audio_transcript_done_event.py +++ b/src/openai/types/responses/response_audio_transcript_done_event.py @@ -8,6 +8,8 @@ class ResponseAudioTranscriptDoneEvent(BaseModel): + """Emitted when the full audio transcript is completed.""" + sequence_number: int """The sequence number of this event.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py b/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py index c5fef939b1..c6bc8b73ea 100644 --- a/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py +++ b/src/openai/types/responses/response_code_interpreter_call_code_delta_event.py @@ -8,6 +8,8 @@ class ResponseCodeInterpreterCallCodeDeltaEvent(BaseModel): + """Emitted when a partial code snippet is streamed by the code interpreter.""" + delta: str """The partial code snippet being streamed by the code interpreter.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_code_done_event.py b/src/openai/types/responses/response_code_interpreter_call_code_done_event.py index 5201a02d36..186c03711a 100644 --- a/src/openai/types/responses/response_code_interpreter_call_code_done_event.py +++ b/src/openai/types/responses/response_code_interpreter_call_code_done_event.py @@ -8,6 +8,8 @@ class ResponseCodeInterpreterCallCodeDoneEvent(BaseModel): + """Emitted when the code snippet is finalized by the code interpreter.""" + code: str """The final code snippet output by the code interpreter.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_completed_event.py b/src/openai/types/responses/response_code_interpreter_call_completed_event.py index bb9563a16b..197e39e7e9 100644 --- a/src/openai/types/responses/response_code_interpreter_call_completed_event.py +++ b/src/openai/types/responses/response_code_interpreter_call_completed_event.py @@ -8,6 +8,8 @@ class ResponseCodeInterpreterCallCompletedEvent(BaseModel): + """Emitted when the code interpreter call is completed.""" + item_id: str """The unique identifier of the code interpreter tool call item.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py b/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py index 9c6b221004..c775f1b864 100644 --- a/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py +++ b/src/openai/types/responses/response_code_interpreter_call_in_progress_event.py @@ -8,6 +8,8 @@ class ResponseCodeInterpreterCallInProgressEvent(BaseModel): + """Emitted when a code interpreter call is in progress.""" + item_id: str """The unique identifier of the code interpreter tool call item.""" diff --git a/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py b/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py index f6191e4165..85e9c87f08 100644 --- a/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py +++ b/src/openai/types/responses/response_code_interpreter_call_interpreting_event.py @@ -8,6 +8,8 @@ class ResponseCodeInterpreterCallInterpretingEvent(BaseModel): + """Emitted when the code interpreter is actively interpreting the code snippet.""" + item_id: str """The unique identifier of the code interpreter tool call item.""" diff --git a/src/openai/types/responses/response_code_interpreter_tool_call.py b/src/openai/types/responses/response_code_interpreter_tool_call.py index b651581520..d7e30f4920 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call.py @@ -10,6 +10,8 @@ class OutputLogs(BaseModel): + """The logs output from the code interpreter.""" + logs: str """The logs output from the code interpreter.""" @@ -18,6 +20,8 @@ class OutputLogs(BaseModel): class OutputImage(BaseModel): + """The image output from the code interpreter.""" + type: Literal["image"] """The type of the output. Always `image`.""" @@ -29,6 +33,8 @@ class OutputImage(BaseModel): class ResponseCodeInterpreterToolCall(BaseModel): + """A tool call to run code.""" + id: str """The unique ID of the code interpreter tool call.""" diff --git a/src/openai/types/responses/response_code_interpreter_tool_call_param.py b/src/openai/types/responses/response_code_interpreter_tool_call_param.py index d402b872a4..fc03a3fe48 100644 --- a/src/openai/types/responses/response_code_interpreter_tool_call_param.py +++ b/src/openai/types/responses/response_code_interpreter_tool_call_param.py @@ -9,6 +9,8 @@ class OutputLogs(TypedDict, total=False): + """The logs output from the code interpreter.""" + logs: Required[str] """The logs output from the code interpreter.""" @@ -17,6 +19,8 @@ class OutputLogs(TypedDict, total=False): class OutputImage(TypedDict, total=False): + """The image output from the code interpreter.""" + type: Required[Literal["image"]] """The type of the output. Always `image`.""" @@ -28,6 +32,8 @@ class OutputImage(TypedDict, total=False): class ResponseCodeInterpreterToolCallParam(TypedDict, total=False): + """A tool call to run code.""" + id: Required[str] """The unique ID of the code interpreter tool call.""" diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index fe38b15a9d..35a390f807 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Union, Iterable, Optional -from typing_extensions import Literal, TypedDict +from typing_extensions import Literal, Required, TypedDict from .response_input_item_param import ResponseInputItemParam @@ -11,6 +11,103 @@ class ResponseCompactParams(TypedDict, total=False): + model: Required[ + Union[ + Literal[ + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ] + ] + """Model ID used to generate the response, like `gpt-5` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + input: Union[str, Iterable[ResponseInputItemParam], None] """Text, image, or file inputs to the model, used to generate a response""" @@ -22,101 +119,6 @@ class ResponseCompactParams(TypedDict, total=False): system (or developer) messages in new responses. """ - model: Union[ - Literal[ - "gpt-5.1", - "gpt-5.1-2025-11-13", - "gpt-5.1-codex", - "gpt-5.1-mini", - "gpt-5.1-chat-latest", - "gpt-5", - "gpt-5-mini", - "gpt-5-nano", - "gpt-5-2025-08-07", - "gpt-5-mini-2025-08-07", - "gpt-5-nano-2025-08-07", - "gpt-5-chat-latest", - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "gpt-4.1-2025-04-14", - "gpt-4.1-mini-2025-04-14", - "gpt-4.1-nano-2025-04-14", - "o4-mini", - "o4-mini-2025-04-16", - "o3", - "o3-2025-04-16", - "o3-mini", - "o3-mini-2025-01-31", - "o1", - "o1-2024-12-17", - "o1-preview", - "o1-preview-2024-09-12", - "o1-mini", - "o1-mini-2024-09-12", - "gpt-4o", - "gpt-4o-2024-11-20", - "gpt-4o-2024-08-06", - "gpt-4o-2024-05-13", - "gpt-4o-audio-preview", - "gpt-4o-audio-preview-2024-10-01", - "gpt-4o-audio-preview-2024-12-17", - "gpt-4o-audio-preview-2025-06-03", - "gpt-4o-mini-audio-preview", - "gpt-4o-mini-audio-preview-2024-12-17", - "gpt-4o-search-preview", - "gpt-4o-mini-search-preview", - "gpt-4o-search-preview-2025-03-11", - "gpt-4o-mini-search-preview-2025-03-11", - "chatgpt-4o-latest", - "codex-mini-latest", - "gpt-4o-mini", - "gpt-4o-mini-2024-07-18", - "gpt-4-turbo", - "gpt-4-turbo-2024-04-09", - "gpt-4-0125-preview", - "gpt-4-turbo-preview", - "gpt-4-1106-preview", - "gpt-4-vision-preview", - "gpt-4", - "gpt-4-0314", - "gpt-4-0613", - "gpt-4-32k", - "gpt-4-32k-0314", - "gpt-4-32k-0613", - "gpt-3.5-turbo", - "gpt-3.5-turbo-16k", - "gpt-3.5-turbo-0301", - "gpt-3.5-turbo-0613", - "gpt-3.5-turbo-1106", - "gpt-3.5-turbo-0125", - "gpt-3.5-turbo-16k-0613", - "o1-pro", - "o1-pro-2025-03-19", - "o3-pro", - "o3-pro-2025-06-10", - "o3-deep-research", - "o3-deep-research-2025-06-26", - "o4-mini-deep-research", - "o4-mini-deep-research-2025-06-26", - "computer-use-preview", - "computer-use-preview-2025-03-11", - "gpt-5-codex", - "gpt-5-pro", - "gpt-5-pro-2025-10-06", - "gpt-5.1-codex-max", - ], - str, - None, - ] - """Model ID used to generate the response, like `gpt-5` or `o3`. - - OpenAI offers a wide range of models with different capabilities, performance - characteristics, and price points. Refer to the - [model guide](https://platform.openai.com/docs/models) to browse and compare - available models. - """ - previous_response_id: Optional[str] """The unique ID of the previous response to the model. diff --git a/src/openai/types/responses/response_compaction_item.py b/src/openai/types/responses/response_compaction_item.py index dc5f839bb8..f5f8b97f4e 100644 --- a/src/openai/types/responses/response_compaction_item.py +++ b/src/openai/types/responses/response_compaction_item.py @@ -9,6 +9,10 @@ class ResponseCompactionItem(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact). + """ + id: str """The unique ID of the compaction item.""" diff --git a/src/openai/types/responses/response_compaction_item_param.py b/src/openai/types/responses/response_compaction_item_param.py index 8fdc2a561a..5dcc921d67 100644 --- a/src/openai/types/responses/response_compaction_item_param.py +++ b/src/openai/types/responses/response_compaction_item_param.py @@ -9,6 +9,10 @@ class ResponseCompactionItemParam(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact). + """ + encrypted_content: str type: Literal["compaction"] diff --git a/src/openai/types/responses/response_compaction_item_param_param.py b/src/openai/types/responses/response_compaction_item_param_param.py index 0d12296589..b9b5ab031c 100644 --- a/src/openai/types/responses/response_compaction_item_param_param.py +++ b/src/openai/types/responses/response_compaction_item_param_param.py @@ -9,6 +9,10 @@ class ResponseCompactionItemParamParam(TypedDict, total=False): + """ + A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact). + """ + encrypted_content: Required[str] type: Required[Literal["compaction"]] diff --git a/src/openai/types/responses/response_completed_event.py b/src/openai/types/responses/response_completed_event.py index 8a2bd51f75..6dc958101c 100644 --- a/src/openai/types/responses/response_completed_event.py +++ b/src/openai/types/responses/response_completed_event.py @@ -9,6 +9,8 @@ class ResponseCompletedEvent(BaseModel): + """Emitted when the model response is complete.""" + response: Response """Properties of the completed response.""" diff --git a/src/openai/types/responses/response_computer_tool_call.py b/src/openai/types/responses/response_computer_tool_call.py index f1476fa0fb..4e1b3cf7fd 100644 --- a/src/openai/types/responses/response_computer_tool_call.py +++ b/src/openai/types/responses/response_computer_tool_call.py @@ -24,6 +24,8 @@ class ActionClick(BaseModel): + """A click action.""" + button: Literal["left", "right", "wheel", "back", "forward"] """Indicates which mouse button was pressed during the click. @@ -41,6 +43,8 @@ class ActionClick(BaseModel): class ActionDoubleClick(BaseModel): + """A double click action.""" + type: Literal["double_click"] """Specifies the event type. @@ -55,6 +59,8 @@ class ActionDoubleClick(BaseModel): class ActionDragPath(BaseModel): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + x: int """The x-coordinate.""" @@ -63,6 +69,8 @@ class ActionDragPath(BaseModel): class ActionDrag(BaseModel): + """A drag action.""" + path: List[ActionDragPath] """An array of coordinates representing the path of the drag action. @@ -84,6 +92,8 @@ class ActionDrag(BaseModel): class ActionKeypress(BaseModel): + """A collection of keypresses the model would like to perform.""" + keys: List[str] """The combination of keys the model is requesting to be pressed. @@ -98,6 +108,8 @@ class ActionKeypress(BaseModel): class ActionMove(BaseModel): + """A mouse move action.""" + type: Literal["move"] """Specifies the event type. @@ -112,6 +124,8 @@ class ActionMove(BaseModel): class ActionScreenshot(BaseModel): + """A screenshot action.""" + type: Literal["screenshot"] """Specifies the event type. @@ -120,6 +134,8 @@ class ActionScreenshot(BaseModel): class ActionScroll(BaseModel): + """A scroll action.""" + scroll_x: int """The horizontal scroll distance.""" @@ -140,6 +156,8 @@ class ActionScroll(BaseModel): class ActionType(BaseModel): + """An action to type in text.""" + text: str """The text to type.""" @@ -151,6 +169,8 @@ class ActionType(BaseModel): class ActionWait(BaseModel): + """A wait action.""" + type: Literal["wait"] """Specifies the event type. @@ -175,6 +195,8 @@ class ActionWait(BaseModel): class PendingSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + id: str """The ID of the pending safety check.""" @@ -186,6 +208,12 @@ class PendingSafetyCheck(BaseModel): class ResponseComputerToolCall(BaseModel): + """A tool call to a computer use tool. + + See the + [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information. + """ + id: str """The unique ID of the computer call.""" diff --git a/src/openai/types/responses/response_computer_tool_call_output_item.py b/src/openai/types/responses/response_computer_tool_call_output_item.py index e1ac358cc6..90e935c3bd 100644 --- a/src/openai/types/responses/response_computer_tool_call_output_item.py +++ b/src/openai/types/responses/response_computer_tool_call_output_item.py @@ -10,6 +10,8 @@ class AcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + id: str """The ID of the pending safety check.""" diff --git a/src/openai/types/responses/response_computer_tool_call_output_screenshot.py b/src/openai/types/responses/response_computer_tool_call_output_screenshot.py index a500da85c1..2c16f215eb 100644 --- a/src/openai/types/responses/response_computer_tool_call_output_screenshot.py +++ b/src/openai/types/responses/response_computer_tool_call_output_screenshot.py @@ -9,6 +9,8 @@ class ResponseComputerToolCallOutputScreenshot(BaseModel): + """A computer screenshot image used with the computer use tool.""" + type: Literal["computer_screenshot"] """Specifies the event type. diff --git a/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py b/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py index efc2028aa4..857ccf9fb9 100644 --- a/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py +++ b/src/openai/types/responses/response_computer_tool_call_output_screenshot_param.py @@ -8,6 +8,8 @@ class ResponseComputerToolCallOutputScreenshotParam(TypedDict, total=False): + """A computer screenshot image used with the computer use tool.""" + type: Required[Literal["computer_screenshot"]] """Specifies the event type. diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py index 228f76bac9..550ba599cd 100644 --- a/src/openai/types/responses/response_computer_tool_call_param.py +++ b/src/openai/types/responses/response_computer_tool_call_param.py @@ -25,6 +25,8 @@ class ActionClick(TypedDict, total=False): + """A click action.""" + button: Required[Literal["left", "right", "wheel", "back", "forward"]] """Indicates which mouse button was pressed during the click. @@ -42,6 +44,8 @@ class ActionClick(TypedDict, total=False): class ActionDoubleClick(TypedDict, total=False): + """A double click action.""" + type: Required[Literal["double_click"]] """Specifies the event type. @@ -56,6 +60,8 @@ class ActionDoubleClick(TypedDict, total=False): class ActionDragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + x: Required[int] """The x-coordinate.""" @@ -64,6 +70,8 @@ class ActionDragPath(TypedDict, total=False): class ActionDrag(TypedDict, total=False): + """A drag action.""" + path: Required[Iterable[ActionDragPath]] """An array of coordinates representing the path of the drag action. @@ -85,6 +93,8 @@ class ActionDrag(TypedDict, total=False): class ActionKeypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + keys: Required[SequenceNotStr[str]] """The combination of keys the model is requesting to be pressed. @@ -99,6 +109,8 @@ class ActionKeypress(TypedDict, total=False): class ActionMove(TypedDict, total=False): + """A mouse move action.""" + type: Required[Literal["move"]] """Specifies the event type. @@ -113,6 +125,8 @@ class ActionMove(TypedDict, total=False): class ActionScreenshot(TypedDict, total=False): + """A screenshot action.""" + type: Required[Literal["screenshot"]] """Specifies the event type. @@ -121,6 +135,8 @@ class ActionScreenshot(TypedDict, total=False): class ActionScroll(TypedDict, total=False): + """A scroll action.""" + scroll_x: Required[int] """The horizontal scroll distance.""" @@ -141,6 +157,8 @@ class ActionScroll(TypedDict, total=False): class ActionType(TypedDict, total=False): + """An action to type in text.""" + text: Required[str] """The text to type.""" @@ -152,6 +170,8 @@ class ActionType(TypedDict, total=False): class ActionWait(TypedDict, total=False): + """A wait action.""" + type: Required[Literal["wait"]] """Specifies the event type. @@ -173,6 +193,8 @@ class ActionWait(TypedDict, total=False): class PendingSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + id: Required[str] """The ID of the pending safety check.""" @@ -184,6 +206,12 @@ class PendingSafetyCheck(TypedDict, total=False): class ResponseComputerToolCallParam(TypedDict, total=False): + """A tool call to a computer use tool. + + See the + [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information. + """ + id: Required[str] """The unique ID of the computer call.""" diff --git a/src/openai/types/responses/response_content_part_added_event.py b/src/openai/types/responses/response_content_part_added_event.py index c78e80d1c4..ec9893159d 100644 --- a/src/openai/types/responses/response_content_part_added_event.py +++ b/src/openai/types/responses/response_content_part_added_event.py @@ -12,6 +12,8 @@ class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + text: str """The reasoning text from the model.""" @@ -25,6 +27,8 @@ class PartReasoningText(BaseModel): class ResponseContentPartAddedEvent(BaseModel): + """Emitted when a new content part is added.""" + content_index: int """The index of the content part that was added.""" diff --git a/src/openai/types/responses/response_content_part_done_event.py b/src/openai/types/responses/response_content_part_done_event.py index 732f2303ef..f896ad8743 100644 --- a/src/openai/types/responses/response_content_part_done_event.py +++ b/src/openai/types/responses/response_content_part_done_event.py @@ -12,6 +12,8 @@ class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + text: str """The reasoning text from the model.""" @@ -25,6 +27,8 @@ class PartReasoningText(BaseModel): class ResponseContentPartDoneEvent(BaseModel): + """Emitted when a content part is done.""" + content_index: int """The index of the content part that is done.""" diff --git a/src/openai/types/responses/response_conversation_param.py b/src/openai/types/responses/response_conversation_param.py index 067bdc7a31..d1587fe68a 100644 --- a/src/openai/types/responses/response_conversation_param.py +++ b/src/openai/types/responses/response_conversation_param.py @@ -8,5 +8,7 @@ class ResponseConversationParam(TypedDict, total=False): + """The conversation that this response belongs to.""" + id: Required[str] """The unique ID of the conversation.""" diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 64888ac62d..15844c6597 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -283,6 +283,8 @@ class ResponseCreateParamsBase(TypedDict, total=False): class StreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + include_obfuscation: bool """When true, stream obfuscation will be enabled. diff --git a/src/openai/types/responses/response_created_event.py b/src/openai/types/responses/response_created_event.py index 73a9d700d4..308b2f4916 100644 --- a/src/openai/types/responses/response_created_event.py +++ b/src/openai/types/responses/response_created_event.py @@ -9,6 +9,8 @@ class ResponseCreatedEvent(BaseModel): + """An event that is emitted when a response is created.""" + response: Response """The response that was created.""" diff --git a/src/openai/types/responses/response_custom_tool_call.py b/src/openai/types/responses/response_custom_tool_call.py index 38c650e662..f05743966e 100644 --- a/src/openai/types/responses/response_custom_tool_call.py +++ b/src/openai/types/responses/response_custom_tool_call.py @@ -9,6 +9,8 @@ class ResponseCustomToolCall(BaseModel): + """A call to a custom tool created by the model.""" + call_id: str """An identifier used to map this custom tool call to a tool call output.""" diff --git a/src/openai/types/responses/response_custom_tool_call_input_delta_event.py b/src/openai/types/responses/response_custom_tool_call_input_delta_event.py index 6c33102d75..7473d33d9a 100644 --- a/src/openai/types/responses/response_custom_tool_call_input_delta_event.py +++ b/src/openai/types/responses/response_custom_tool_call_input_delta_event.py @@ -8,6 +8,8 @@ class ResponseCustomToolCallInputDeltaEvent(BaseModel): + """Event representing a delta (partial update) to the input of a custom tool call.""" + delta: str """The incremental input data (delta) for the custom tool call.""" diff --git a/src/openai/types/responses/response_custom_tool_call_input_done_event.py b/src/openai/types/responses/response_custom_tool_call_input_done_event.py index 35a2fee22b..be47ae8e96 100644 --- a/src/openai/types/responses/response_custom_tool_call_input_done_event.py +++ b/src/openai/types/responses/response_custom_tool_call_input_done_event.py @@ -8,6 +8,8 @@ class ResponseCustomToolCallInputDoneEvent(BaseModel): + """Event indicating that input for a custom tool call is complete.""" + input: str """The complete input data for the custom tool call.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output.py b/src/openai/types/responses/response_custom_tool_call_output.py index 9db9e7e5cf..833956493b 100644 --- a/src/openai/types/responses/response_custom_tool_call_output.py +++ b/src/openai/types/responses/response_custom_tool_call_output.py @@ -17,6 +17,8 @@ class ResponseCustomToolCallOutput(BaseModel): + """The output of a custom tool call from your code, being sent back to the model.""" + call_id: str """The call ID, used to map this custom tool call output to a custom tool call.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output_param.py b/src/openai/types/responses/response_custom_tool_call_output_param.py index e967a37cff..db0034216a 100644 --- a/src/openai/types/responses/response_custom_tool_call_output_param.py +++ b/src/openai/types/responses/response_custom_tool_call_output_param.py @@ -15,6 +15,8 @@ class ResponseCustomToolCallOutputParam(TypedDict, total=False): + """The output of a custom tool call from your code, being sent back to the model.""" + call_id: Required[str] """The call ID, used to map this custom tool call output to a custom tool call.""" diff --git a/src/openai/types/responses/response_custom_tool_call_param.py b/src/openai/types/responses/response_custom_tool_call_param.py index e15beac29f..5d4ce3376c 100644 --- a/src/openai/types/responses/response_custom_tool_call_param.py +++ b/src/openai/types/responses/response_custom_tool_call_param.py @@ -8,6 +8,8 @@ class ResponseCustomToolCallParam(TypedDict, total=False): + """A call to a custom tool created by the model.""" + call_id: Required[str] """An identifier used to map this custom tool call to a tool call output.""" diff --git a/src/openai/types/responses/response_error.py b/src/openai/types/responses/response_error.py index 90f1fcf5da..90958d1c13 100644 --- a/src/openai/types/responses/response_error.py +++ b/src/openai/types/responses/response_error.py @@ -8,6 +8,8 @@ class ResponseError(BaseModel): + """An error object returned when the model fails to generate a Response.""" + code: Literal[ "server_error", "rate_limit_exceeded", diff --git a/src/openai/types/responses/response_error_event.py b/src/openai/types/responses/response_error_event.py index 826c395125..1789f731b4 100644 --- a/src/openai/types/responses/response_error_event.py +++ b/src/openai/types/responses/response_error_event.py @@ -9,6 +9,8 @@ class ResponseErrorEvent(BaseModel): + """Emitted when an error occurs.""" + code: Optional[str] = None """The error code.""" diff --git a/src/openai/types/responses/response_failed_event.py b/src/openai/types/responses/response_failed_event.py index cdd3d7d808..2232c9678d 100644 --- a/src/openai/types/responses/response_failed_event.py +++ b/src/openai/types/responses/response_failed_event.py @@ -9,6 +9,8 @@ class ResponseFailedEvent(BaseModel): + """An event that is emitted when a response fails.""" + response: Response """The response that failed.""" diff --git a/src/openai/types/responses/response_file_search_call_completed_event.py b/src/openai/types/responses/response_file_search_call_completed_event.py index 08e51b2d3f..88ffa5ac56 100644 --- a/src/openai/types/responses/response_file_search_call_completed_event.py +++ b/src/openai/types/responses/response_file_search_call_completed_event.py @@ -8,6 +8,8 @@ class ResponseFileSearchCallCompletedEvent(BaseModel): + """Emitted when a file search call is completed (results found).""" + item_id: str """The ID of the output item that the file search call is initiated.""" diff --git a/src/openai/types/responses/response_file_search_call_in_progress_event.py b/src/openai/types/responses/response_file_search_call_in_progress_event.py index 63840a649f..4f3504fda4 100644 --- a/src/openai/types/responses/response_file_search_call_in_progress_event.py +++ b/src/openai/types/responses/response_file_search_call_in_progress_event.py @@ -8,6 +8,8 @@ class ResponseFileSearchCallInProgressEvent(BaseModel): + """Emitted when a file search call is initiated.""" + item_id: str """The ID of the output item that the file search call is initiated.""" diff --git a/src/openai/types/responses/response_file_search_call_searching_event.py b/src/openai/types/responses/response_file_search_call_searching_event.py index 706c8c57ad..5bf1a076dd 100644 --- a/src/openai/types/responses/response_file_search_call_searching_event.py +++ b/src/openai/types/responses/response_file_search_call_searching_event.py @@ -8,6 +8,8 @@ class ResponseFileSearchCallSearchingEvent(BaseModel): + """Emitted when a file search is currently searching.""" + item_id: str """The ID of the output item that the file search call is initiated.""" diff --git a/src/openai/types/responses/response_file_search_tool_call.py b/src/openai/types/responses/response_file_search_tool_call.py index ef1c6a5608..fa45631345 100644 --- a/src/openai/types/responses/response_file_search_tool_call.py +++ b/src/openai/types/responses/response_file_search_tool_call.py @@ -32,6 +32,12 @@ class Result(BaseModel): class ResponseFileSearchToolCall(BaseModel): + """The results of a file search tool call. + + See the + [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information. + """ + id: str """The unique ID of the file search tool call.""" diff --git a/src/openai/types/responses/response_file_search_tool_call_param.py b/src/openai/types/responses/response_file_search_tool_call_param.py index 4903dca4fb..45a5bbb486 100644 --- a/src/openai/types/responses/response_file_search_tool_call_param.py +++ b/src/openai/types/responses/response_file_search_tool_call_param.py @@ -34,6 +34,12 @@ class Result(TypedDict, total=False): class ResponseFileSearchToolCallParam(TypedDict, total=False): + """The results of a file search tool call. + + See the + [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information. + """ + id: Required[str] """The unique ID of the file search tool call.""" diff --git a/src/openai/types/responses/response_format_text_json_schema_config.py b/src/openai/types/responses/response_format_text_json_schema_config.py index 001fcf5bab..b953112621 100644 --- a/src/openai/types/responses/response_format_text_json_schema_config.py +++ b/src/openai/types/responses/response_format_text_json_schema_config.py @@ -11,6 +11,12 @@ class ResponseFormatTextJSONSchemaConfig(BaseModel): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs). + """ + name: str """The name of the response format. diff --git a/src/openai/types/responses/response_format_text_json_schema_config_param.py b/src/openai/types/responses/response_format_text_json_schema_config_param.py index f293a80c5a..6f5c633106 100644 --- a/src/openai/types/responses/response_format_text_json_schema_config_param.py +++ b/src/openai/types/responses/response_format_text_json_schema_config_param.py @@ -9,6 +9,12 @@ class ResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs). + """ + name: Required[str] """The name of the response format. diff --git a/src/openai/types/responses/response_function_call_arguments_delta_event.py b/src/openai/types/responses/response_function_call_arguments_delta_event.py index c6bc5dfad7..0798c2e123 100644 --- a/src/openai/types/responses/response_function_call_arguments_delta_event.py +++ b/src/openai/types/responses/response_function_call_arguments_delta_event.py @@ -8,6 +8,8 @@ class ResponseFunctionCallArgumentsDeltaEvent(BaseModel): + """Emitted when there is a partial function-call arguments delta.""" + delta: str """The function-call arguments delta that is added.""" diff --git a/src/openai/types/responses/response_function_call_arguments_done_event.py b/src/openai/types/responses/response_function_call_arguments_done_event.py index 4ee5ed7fe1..543cd073a2 100644 --- a/src/openai/types/responses/response_function_call_arguments_done_event.py +++ b/src/openai/types/responses/response_function_call_arguments_done_event.py @@ -8,6 +8,8 @@ class ResponseFunctionCallArgumentsDoneEvent(BaseModel): + """Emitted when function-call arguments are finalized.""" + arguments: str """The function-call arguments.""" diff --git a/src/openai/types/responses/response_function_shell_call_output_content.py b/src/openai/types/responses/response_function_shell_call_output_content.py index e0e2c09ad1..dae48f14da 100644 --- a/src/openai/types/responses/response_function_shell_call_output_content.py +++ b/src/openai/types/responses/response_function_shell_call_output_content.py @@ -10,11 +10,15 @@ class OutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + type: Literal["timeout"] """The outcome type. Always `timeout`.""" class OutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + exit_code: int """The exit code returned by the shell process.""" @@ -26,6 +30,8 @@ class OutcomeExit(BaseModel): class ResponseFunctionShellCallOutputContent(BaseModel): + """Captured stdout and stderr for a portion of a shell tool call output.""" + outcome: Outcome """The exit or timeout outcome associated with this shell call.""" diff --git a/src/openai/types/responses/response_function_shell_call_output_content_param.py b/src/openai/types/responses/response_function_shell_call_output_content_param.py index fa065bd4b5..4d8ea70d08 100644 --- a/src/openai/types/responses/response_function_shell_call_output_content_param.py +++ b/src/openai/types/responses/response_function_shell_call_output_content_param.py @@ -9,11 +9,15 @@ class OutcomeTimeout(TypedDict, total=False): + """Indicates that the shell call exceeded its configured time limit.""" + type: Required[Literal["timeout"]] """The outcome type. Always `timeout`.""" class OutcomeExit(TypedDict, total=False): + """Indicates that the shell commands finished and returned an exit code.""" + exit_code: Required[int] """The exit code returned by the shell process.""" @@ -25,6 +29,8 @@ class OutcomeExit(TypedDict, total=False): class ResponseFunctionShellCallOutputContentParam(TypedDict, total=False): + """Captured stdout and stderr for a portion of a shell tool call output.""" + outcome: Required[Outcome] """The exit or timeout outcome associated with this shell call.""" diff --git a/src/openai/types/responses/response_function_shell_tool_call.py b/src/openai/types/responses/response_function_shell_tool_call.py index de42cb0640..7c6a184ed4 100644 --- a/src/openai/types/responses/response_function_shell_tool_call.py +++ b/src/openai/types/responses/response_function_shell_tool_call.py @@ -9,6 +9,8 @@ class Action(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + commands: List[str] max_output_length: Optional[int] = None @@ -19,6 +21,8 @@ class Action(BaseModel): class ResponseFunctionShellToolCall(BaseModel): + """A tool call that executes one or more shell commands in a managed environment.""" + id: str """The unique ID of the shell tool call. diff --git a/src/openai/types/responses/response_function_shell_tool_call_output.py b/src/openai/types/responses/response_function_shell_tool_call_output.py index e74927df41..7885ee2f83 100644 --- a/src/openai/types/responses/response_function_shell_tool_call_output.py +++ b/src/openai/types/responses/response_function_shell_tool_call_output.py @@ -16,11 +16,15 @@ class OutputOutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + type: Literal["timeout"] """The outcome type. Always `timeout`.""" class OutputOutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + exit_code: int """Exit code from the shell process.""" @@ -32,6 +36,8 @@ class OutputOutcomeExit(BaseModel): class Output(BaseModel): + """The content of a shell call output.""" + outcome: OutputOutcome """ Represents either an exit outcome (with an exit code) or a timeout outcome for a @@ -46,6 +52,8 @@ class Output(BaseModel): class ResponseFunctionShellToolCallOutput(BaseModel): + """The output of a shell tool call.""" + id: str """The unique ID of the shell call output. diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py index 2a8482204e..194e3f7d6a 100644 --- a/src/openai/types/responses/response_function_tool_call.py +++ b/src/openai/types/responses/response_function_tool_call.py @@ -9,6 +9,12 @@ class ResponseFunctionToolCall(BaseModel): + """A tool call to run a function. + + See the + [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information. + """ + arguments: str """A JSON string of the arguments to pass to the function.""" diff --git a/src/openai/types/responses/response_function_tool_call_item.py b/src/openai/types/responses/response_function_tool_call_item.py index 762015a4b1..3df299e512 100644 --- a/src/openai/types/responses/response_function_tool_call_item.py +++ b/src/openai/types/responses/response_function_tool_call_item.py @@ -6,5 +6,11 @@ class ResponseFunctionToolCallItem(ResponseFunctionToolCall): + """A tool call to run a function. + + See the + [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information. + """ + id: str # type: ignore """The unique ID of the function tool call.""" diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py index eaa263cf67..4e8dd3d629 100644 --- a/src/openai/types/responses/response_function_tool_call_param.py +++ b/src/openai/types/responses/response_function_tool_call_param.py @@ -8,6 +8,12 @@ class ResponseFunctionToolCallParam(TypedDict, total=False): + """A tool call to run a function. + + See the + [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information. + """ + arguments: Required[str] """A JSON string of the arguments to pass to the function.""" diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index f3e80e6a8f..1450fba4d1 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -10,6 +10,8 @@ class ActionSearchSource(BaseModel): + """A source used in the search.""" + type: Literal["url"] """The type of source. Always `url`.""" @@ -18,6 +20,8 @@ class ActionSearchSource(BaseModel): class ActionSearch(BaseModel): + """Action type "search" - Performs a web search query.""" + query: str """The search query.""" @@ -29,6 +33,8 @@ class ActionSearch(BaseModel): class ActionOpenPage(BaseModel): + """Action type "open_page" - Opens a specific URL from search results.""" + type: Literal["open_page"] """The action type.""" @@ -37,6 +43,8 @@ class ActionOpenPage(BaseModel): class ActionFind(BaseModel): + """Action type "find": Searches for a pattern within a loaded page.""" + pattern: str """The pattern or text to search for within the page.""" @@ -51,6 +59,12 @@ class ActionFind(BaseModel): class ResponseFunctionWebSearch(BaseModel): + """The results of a web search tool call. + + See the + [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information. + """ + id: str """The unique ID of the web search tool call.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index fc019d3eb7..8d0b60334d 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -16,6 +16,8 @@ class ActionSearchSource(TypedDict, total=False): + """A source used in the search.""" + type: Required[Literal["url"]] """The type of source. Always `url`.""" @@ -24,6 +26,8 @@ class ActionSearchSource(TypedDict, total=False): class ActionSearch(TypedDict, total=False): + """Action type "search" - Performs a web search query.""" + query: Required[str] """The search query.""" @@ -35,6 +39,8 @@ class ActionSearch(TypedDict, total=False): class ActionOpenPage(TypedDict, total=False): + """Action type "open_page" - Opens a specific URL from search results.""" + type: Required[Literal["open_page"]] """The action type.""" @@ -43,6 +49,8 @@ class ActionOpenPage(TypedDict, total=False): class ActionFind(TypedDict, total=False): + """Action type "find": Searches for a pattern within a loaded page.""" + pattern: Required[str] """The pattern or text to search for within the page.""" @@ -57,6 +65,12 @@ class ActionFind(TypedDict, total=False): class ResponseFunctionWebSearchParam(TypedDict, total=False): + """The results of a web search tool call. + + See the + [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information. + """ + id: Required[str] """The unique ID of the web search tool call.""" diff --git a/src/openai/types/responses/response_image_gen_call_completed_event.py b/src/openai/types/responses/response_image_gen_call_completed_event.py index a554273ed0..f6ce9d0fd8 100644 --- a/src/openai/types/responses/response_image_gen_call_completed_event.py +++ b/src/openai/types/responses/response_image_gen_call_completed_event.py @@ -8,6 +8,10 @@ class ResponseImageGenCallCompletedEvent(BaseModel): + """ + Emitted when an image generation tool call has completed and the final image is available. + """ + item_id: str """The unique identifier of the image generation item being processed.""" diff --git a/src/openai/types/responses/response_image_gen_call_generating_event.py b/src/openai/types/responses/response_image_gen_call_generating_event.py index 74b4f57333..8e3026d0dc 100644 --- a/src/openai/types/responses/response_image_gen_call_generating_event.py +++ b/src/openai/types/responses/response_image_gen_call_generating_event.py @@ -8,6 +8,10 @@ class ResponseImageGenCallGeneratingEvent(BaseModel): + """ + Emitted when an image generation tool call is actively generating an image (intermediate state). + """ + item_id: str """The unique identifier of the image generation item being processed.""" diff --git a/src/openai/types/responses/response_image_gen_call_in_progress_event.py b/src/openai/types/responses/response_image_gen_call_in_progress_event.py index b36ff5fa47..60726a22b4 100644 --- a/src/openai/types/responses/response_image_gen_call_in_progress_event.py +++ b/src/openai/types/responses/response_image_gen_call_in_progress_event.py @@ -8,6 +8,8 @@ class ResponseImageGenCallInProgressEvent(BaseModel): + """Emitted when an image generation tool call is in progress.""" + item_id: str """The unique identifier of the image generation item being processed.""" diff --git a/src/openai/types/responses/response_image_gen_call_partial_image_event.py b/src/openai/types/responses/response_image_gen_call_partial_image_event.py index e69c95fb33..289d5d44c0 100644 --- a/src/openai/types/responses/response_image_gen_call_partial_image_event.py +++ b/src/openai/types/responses/response_image_gen_call_partial_image_event.py @@ -8,6 +8,8 @@ class ResponseImageGenCallPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image generation streaming.""" + item_id: str """The unique identifier of the image generation item being processed.""" diff --git a/src/openai/types/responses/response_in_progress_event.py b/src/openai/types/responses/response_in_progress_event.py index b82e10b357..9d9bbd94b0 100644 --- a/src/openai/types/responses/response_in_progress_event.py +++ b/src/openai/types/responses/response_in_progress_event.py @@ -9,6 +9,8 @@ class ResponseInProgressEvent(BaseModel): + """Emitted when the response is in progress.""" + response: Response """The response that is in progress.""" diff --git a/src/openai/types/responses/response_incomplete_event.py b/src/openai/types/responses/response_incomplete_event.py index 63c969a428..ef99c5f0b2 100644 --- a/src/openai/types/responses/response_incomplete_event.py +++ b/src/openai/types/responses/response_incomplete_event.py @@ -9,6 +9,8 @@ class ResponseIncompleteEvent(BaseModel): + """An event that is emitted when a response finishes as incomplete.""" + response: Response """The response that was incomplete.""" diff --git a/src/openai/types/responses/response_input_audio.py b/src/openai/types/responses/response_input_audio.py index 9fef6de0fd..f362ba4133 100644 --- a/src/openai/types/responses/response_input_audio.py +++ b/src/openai/types/responses/response_input_audio.py @@ -16,6 +16,8 @@ class InputAudio(BaseModel): class ResponseInputAudio(BaseModel): + """An audio input to the model.""" + input_audio: InputAudio type: Literal["input_audio"] diff --git a/src/openai/types/responses/response_input_audio_param.py b/src/openai/types/responses/response_input_audio_param.py index f3fc913cca..0be935c54d 100644 --- a/src/openai/types/responses/response_input_audio_param.py +++ b/src/openai/types/responses/response_input_audio_param.py @@ -16,6 +16,8 @@ class InputAudio(TypedDict, total=False): class ResponseInputAudioParam(TypedDict, total=False): + """An audio input to the model.""" + input_audio: Required[InputAudio] type: Required[Literal["input_audio"]] diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py index 1eecd6a2b6..3e5fb70c5f 100644 --- a/src/openai/types/responses/response_input_file.py +++ b/src/openai/types/responses/response_input_file.py @@ -9,6 +9,8 @@ class ResponseInputFile(BaseModel): + """A file input to the model.""" + type: Literal["input_file"] """The type of the input item. Always `input_file`.""" diff --git a/src/openai/types/responses/response_input_file_content.py b/src/openai/types/responses/response_input_file_content.py index d832bb0e26..f0dfef55d0 100644 --- a/src/openai/types/responses/response_input_file_content.py +++ b/src/openai/types/responses/response_input_file_content.py @@ -9,6 +9,8 @@ class ResponseInputFileContent(BaseModel): + """A file input to the model.""" + type: Literal["input_file"] """The type of the input item. Always `input_file`.""" diff --git a/src/openai/types/responses/response_input_file_content_param.py b/src/openai/types/responses/response_input_file_content_param.py index 71f7b3a281..376f6c7a45 100644 --- a/src/openai/types/responses/response_input_file_content_param.py +++ b/src/openai/types/responses/response_input_file_content_param.py @@ -9,6 +9,8 @@ class ResponseInputFileContentParam(TypedDict, total=False): + """A file input to the model.""" + type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py index 0b5f513ec6..8b5da20245 100644 --- a/src/openai/types/responses/response_input_file_param.py +++ b/src/openai/types/responses/response_input_file_param.py @@ -9,6 +9,8 @@ class ResponseInputFileParam(TypedDict, total=False): + """A file input to the model.""" + type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py index f2d760b25e..500bc4b346 100644 --- a/src/openai/types/responses/response_input_image.py +++ b/src/openai/types/responses/response_input_image.py @@ -9,6 +9,11 @@ class ResponseInputImage(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision). + """ + detail: Literal["low", "high", "auto"] """The detail level of the image to be sent to the model. diff --git a/src/openai/types/responses/response_input_image_content.py b/src/openai/types/responses/response_input_image_content.py index fb90cb57eb..e38bc28d5e 100644 --- a/src/openai/types/responses/response_input_image_content.py +++ b/src/openai/types/responses/response_input_image_content.py @@ -9,6 +9,11 @@ class ResponseInputImageContent(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision) + """ + type: Literal["input_image"] """The type of the input item. Always `input_image`.""" diff --git a/src/openai/types/responses/response_input_image_content_param.py b/src/openai/types/responses/response_input_image_content_param.py index c51509a3f3..c21f46d736 100644 --- a/src/openai/types/responses/response_input_image_content_param.py +++ b/src/openai/types/responses/response_input_image_content_param.py @@ -9,6 +9,11 @@ class ResponseInputImageContentParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision) + """ + type: Required[Literal["input_image"]] """The type of the input item. Always `input_image`.""" diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py index bc17e4f1c2..fd8c1bd070 100644 --- a/src/openai/types/responses/response_input_image_param.py +++ b/src/openai/types/responses/response_input_image_param.py @@ -9,6 +9,11 @@ class ResponseInputImageParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision). + """ + detail: Required[Literal["low", "high", "auto"]] """The detail level of the image to be sent to the model. diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index 103c8634ce..23eb2c8950 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -50,6 +50,12 @@ class Message(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + content: ResponseInputMessageContentList """ A list of one or many input items to the model, containing different content @@ -71,6 +77,8 @@ class Message(BaseModel): class ComputerCallOutputAcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + id: str """The ID of the pending safety check.""" @@ -82,6 +90,8 @@ class ComputerCallOutputAcknowledgedSafetyCheck(BaseModel): class ComputerCallOutput(BaseModel): + """The output of a computer tool call.""" + call_id: str """The ID of the computer tool call that produced the output.""" @@ -109,6 +119,8 @@ class ComputerCallOutput(BaseModel): class FunctionCallOutput(BaseModel): + """The output of a function tool call.""" + call_id: str """The unique ID of the function tool call generated by the model.""" @@ -133,6 +145,8 @@ class FunctionCallOutput(BaseModel): class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + id: str """The unique ID of the image generation call.""" @@ -147,6 +161,8 @@ class ImageGenerationCall(BaseModel): class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + command: List[str] """The command to run.""" @@ -167,6 +183,8 @@ class LocalShellCallAction(BaseModel): class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + id: str """The unique ID of the local shell call.""" @@ -184,6 +202,8 @@ class LocalShellCall(BaseModel): class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + id: str """The unique ID of the local shell tool call generated by the model.""" @@ -198,6 +218,8 @@ class LocalShellCallOutput(BaseModel): class ShellCallAction(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + commands: List[str] """Ordered shell commands for the execution environment to run.""" @@ -212,6 +234,8 @@ class ShellCallAction(BaseModel): class ShellCall(BaseModel): + """A tool representing a request to execute one or more shell commands.""" + action: ShellCallAction """The shell commands and limits that describe how to run the tool call.""" @@ -235,6 +259,8 @@ class ShellCall(BaseModel): class ShellCallOutput(BaseModel): + """The streamed output items emitted by a shell tool call.""" + call_id: str """The unique ID of the shell tool call generated by the model.""" @@ -261,6 +287,8 @@ class ShellCallOutput(BaseModel): class ApplyPatchCallOperationCreateFile(BaseModel): + """Instruction for creating a new file via the apply_patch tool.""" + diff: str """Unified diff content to apply when creating the file.""" @@ -272,6 +300,8 @@ class ApplyPatchCallOperationCreateFile(BaseModel): class ApplyPatchCallOperationDeleteFile(BaseModel): + """Instruction for deleting an existing file via the apply_patch tool.""" + path: str """Path of the file to delete relative to the workspace root.""" @@ -280,6 +310,8 @@ class ApplyPatchCallOperationDeleteFile(BaseModel): class ApplyPatchCallOperationUpdateFile(BaseModel): + """Instruction for updating an existing file via the apply_patch tool.""" + diff: str """Unified diff content to apply to the existing file.""" @@ -297,6 +329,10 @@ class ApplyPatchCallOperationUpdateFile(BaseModel): class ApplyPatchCall(BaseModel): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + call_id: str """The unique ID of the apply patch tool call generated by the model.""" @@ -320,6 +356,8 @@ class ApplyPatchCall(BaseModel): class ApplyPatchCallOutput(BaseModel): + """The streamed output emitted by an apply patch tool call.""" + call_id: str """The unique ID of the apply patch tool call generated by the model.""" @@ -343,6 +381,8 @@ class ApplyPatchCallOutput(BaseModel): class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + input_schema: object """The JSON schema describing the tool's input.""" @@ -357,6 +397,8 @@ class McpListToolsTool(BaseModel): class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + id: str """The unique ID of the list.""" @@ -374,6 +416,8 @@ class McpListTools(BaseModel): class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + id: str """The unique ID of the approval request.""" @@ -391,6 +435,8 @@ class McpApprovalRequest(BaseModel): class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + approval_request_id: str """The ID of the approval request being answered.""" @@ -408,6 +454,8 @@ class McpApprovalResponse(BaseModel): class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + id: str """The unique ID of the tool call.""" @@ -444,6 +492,8 @@ class McpCall(BaseModel): class ItemReference(BaseModel): + """An internal identifier for an item to reference.""" + id: str """The ID of the item to reference.""" diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 85d9f92b23..2c42b93021 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -51,6 +51,12 @@ class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + content: Required[ResponseInputMessageContentListParam] """ A list of one or many input items to the model, containing different content @@ -72,6 +78,8 @@ class Message(TypedDict, total=False): class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + id: Required[str] """The ID of the pending safety check.""" @@ -83,6 +91,8 @@ class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + call_id: Required[str] """The ID of the computer tool call that produced the output.""" @@ -110,6 +120,8 @@ class ComputerCallOutput(TypedDict, total=False): class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + call_id: Required[str] """The unique ID of the function tool call generated by the model.""" @@ -134,6 +146,8 @@ class FunctionCallOutput(TypedDict, total=False): class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + id: Required[str] """The unique ID of the image generation call.""" @@ -148,6 +162,8 @@ class ImageGenerationCall(TypedDict, total=False): class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + command: Required[SequenceNotStr[str]] """The command to run.""" @@ -168,6 +184,8 @@ class LocalShellCallAction(TypedDict, total=False): class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + id: Required[str] """The unique ID of the local shell call.""" @@ -185,6 +203,8 @@ class LocalShellCall(TypedDict, total=False): class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + id: Required[str] """The unique ID of the local shell tool call generated by the model.""" @@ -199,6 +219,8 @@ class LocalShellCallOutput(TypedDict, total=False): class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + commands: Required[SequenceNotStr[str]] """Ordered shell commands for the execution environment to run.""" @@ -213,6 +235,8 @@ class ShellCallAction(TypedDict, total=False): class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + action: Required[ShellCallAction] """The shell commands and limits that describe how to run the tool call.""" @@ -236,6 +260,8 @@ class ShellCall(TypedDict, total=False): class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + call_id: Required[str] """The unique ID of the shell tool call generated by the model.""" @@ -262,6 +288,8 @@ class ShellCallOutput(TypedDict, total=False): class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + diff: Required[str] """Unified diff content to apply when creating the file.""" @@ -273,6 +301,8 @@ class ApplyPatchCallOperationCreateFile(TypedDict, total=False): class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + path: Required[str] """Path of the file to delete relative to the workspace root.""" @@ -281,6 +311,8 @@ class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + diff: Required[str] """Unified diff content to apply to the existing file.""" @@ -297,6 +329,10 @@ class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + call_id: Required[str] """The unique ID of the apply patch tool call generated by the model.""" @@ -320,6 +356,8 @@ class ApplyPatchCall(TypedDict, total=False): class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + call_id: Required[str] """The unique ID of the apply patch tool call generated by the model.""" @@ -343,6 +381,8 @@ class ApplyPatchCallOutput(TypedDict, total=False): class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + input_schema: Required[object] """The JSON schema describing the tool's input.""" @@ -357,6 +397,8 @@ class McpListToolsTool(TypedDict, total=False): class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + id: Required[str] """The unique ID of the list.""" @@ -374,6 +416,8 @@ class McpListTools(TypedDict, total=False): class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + id: Required[str] """The unique ID of the approval request.""" @@ -391,6 +435,8 @@ class McpApprovalRequest(TypedDict, total=False): class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + approval_request_id: Required[str] """The ID of the approval request being answered.""" @@ -408,6 +454,8 @@ class McpApprovalResponse(TypedDict, total=False): class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + id: Required[str] """The unique ID of the tool call.""" @@ -444,6 +492,8 @@ class McpCall(TypedDict, total=False): class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + id: Required[str] """The ID of the item to reference.""" diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index bbd8e6af79..c2d12c0ab4 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -52,6 +52,12 @@ class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + content: Required[ResponseInputMessageContentListParam] """ A list of one or many input items to the model, containing different content @@ -73,6 +79,8 @@ class Message(TypedDict, total=False): class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + id: Required[str] """The ID of the pending safety check.""" @@ -84,6 +92,8 @@ class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + call_id: Required[str] """The ID of the computer tool call that produced the output.""" @@ -111,6 +121,8 @@ class ComputerCallOutput(TypedDict, total=False): class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + call_id: Required[str] """The unique ID of the function tool call generated by the model.""" @@ -135,6 +147,8 @@ class FunctionCallOutput(TypedDict, total=False): class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + id: Required[str] """The unique ID of the image generation call.""" @@ -149,6 +163,8 @@ class ImageGenerationCall(TypedDict, total=False): class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + command: Required[SequenceNotStr[str]] """The command to run.""" @@ -169,6 +185,8 @@ class LocalShellCallAction(TypedDict, total=False): class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + id: Required[str] """The unique ID of the local shell call.""" @@ -186,6 +204,8 @@ class LocalShellCall(TypedDict, total=False): class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + id: Required[str] """The unique ID of the local shell tool call generated by the model.""" @@ -200,6 +220,8 @@ class LocalShellCallOutput(TypedDict, total=False): class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + commands: Required[SequenceNotStr[str]] """Ordered shell commands for the execution environment to run.""" @@ -214,6 +236,8 @@ class ShellCallAction(TypedDict, total=False): class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + action: Required[ShellCallAction] """The shell commands and limits that describe how to run the tool call.""" @@ -237,6 +261,8 @@ class ShellCall(TypedDict, total=False): class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + call_id: Required[str] """The unique ID of the shell tool call generated by the model.""" @@ -263,6 +289,8 @@ class ShellCallOutput(TypedDict, total=False): class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + diff: Required[str] """Unified diff content to apply when creating the file.""" @@ -274,6 +302,8 @@ class ApplyPatchCallOperationCreateFile(TypedDict, total=False): class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + path: Required[str] """Path of the file to delete relative to the workspace root.""" @@ -282,6 +312,8 @@ class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + diff: Required[str] """Unified diff content to apply to the existing file.""" @@ -298,6 +330,10 @@ class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + call_id: Required[str] """The unique ID of the apply patch tool call generated by the model.""" @@ -321,6 +357,8 @@ class ApplyPatchCall(TypedDict, total=False): class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + call_id: Required[str] """The unique ID of the apply patch tool call generated by the model.""" @@ -344,6 +382,8 @@ class ApplyPatchCallOutput(TypedDict, total=False): class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + input_schema: Required[object] """The JSON schema describing the tool's input.""" @@ -358,6 +398,8 @@ class McpListToolsTool(TypedDict, total=False): class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + id: Required[str] """The unique ID of the list.""" @@ -375,6 +417,8 @@ class McpListTools(TypedDict, total=False): class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + id: Required[str] """The unique ID of the approval request.""" @@ -392,6 +436,8 @@ class McpApprovalRequest(TypedDict, total=False): class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + approval_request_id: Required[str] """The ID of the approval request being answered.""" @@ -409,6 +455,8 @@ class McpApprovalResponse(TypedDict, total=False): class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + id: Required[str] """The unique ID of the tool call.""" @@ -445,6 +493,8 @@ class McpCall(TypedDict, total=False): class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + id: Required[str] """The ID of the item to reference.""" diff --git a/src/openai/types/responses/response_input_text.py b/src/openai/types/responses/response_input_text.py index ba8d1ea18b..1e06ba71f3 100644 --- a/src/openai/types/responses/response_input_text.py +++ b/src/openai/types/responses/response_input_text.py @@ -8,6 +8,8 @@ class ResponseInputText(BaseModel): + """A text input to the model.""" + text: str """The text input to the model.""" diff --git a/src/openai/types/responses/response_input_text_content.py b/src/openai/types/responses/response_input_text_content.py index 2cce849855..66dbb8b0d0 100644 --- a/src/openai/types/responses/response_input_text_content.py +++ b/src/openai/types/responses/response_input_text_content.py @@ -8,6 +8,8 @@ class ResponseInputTextContent(BaseModel): + """A text input to the model.""" + text: str """The text input to the model.""" diff --git a/src/openai/types/responses/response_input_text_content_param.py b/src/openai/types/responses/response_input_text_content_param.py index 85b57df2bd..013f22d0df 100644 --- a/src/openai/types/responses/response_input_text_content_param.py +++ b/src/openai/types/responses/response_input_text_content_param.py @@ -8,6 +8,8 @@ class ResponseInputTextContentParam(TypedDict, total=False): + """A text input to the model.""" + text: Required[str] """The text input to the model.""" diff --git a/src/openai/types/responses/response_input_text_param.py b/src/openai/types/responses/response_input_text_param.py index f2ba834082..e1a2976e2e 100644 --- a/src/openai/types/responses/response_input_text_param.py +++ b/src/openai/types/responses/response_input_text_param.py @@ -8,6 +8,8 @@ class ResponseInputTextParam(TypedDict, total=False): + """A text input to the model.""" + text: Required[str] """The text input to the model.""" diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py index 5ae2405988..3dba681d53 100644 --- a/src/openai/types/responses/response_item.py +++ b/src/openai/types/responses/response_item.py @@ -34,6 +34,8 @@ class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + id: str """The unique ID of the image generation call.""" @@ -48,6 +50,8 @@ class ImageGenerationCall(BaseModel): class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + command: List[str] """The command to run.""" @@ -68,6 +72,8 @@ class LocalShellCallAction(BaseModel): class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + id: str """The unique ID of the local shell call.""" @@ -85,6 +91,8 @@ class LocalShellCall(BaseModel): class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + id: str """The unique ID of the local shell tool call generated by the model.""" @@ -99,6 +107,8 @@ class LocalShellCallOutput(BaseModel): class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + input_schema: object """The JSON schema describing the tool's input.""" @@ -113,6 +123,8 @@ class McpListToolsTool(BaseModel): class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + id: str """The unique ID of the list.""" @@ -130,6 +142,8 @@ class McpListTools(BaseModel): class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + id: str """The unique ID of the approval request.""" @@ -147,6 +161,8 @@ class McpApprovalRequest(BaseModel): class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + id: str """The unique ID of the approval response""" @@ -164,6 +180,8 @@ class McpApprovalResponse(BaseModel): class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + id: str """The unique ID of the tool call.""" diff --git a/src/openai/types/responses/response_item_list.py b/src/openai/types/responses/response_item_list.py index b43eacdb51..e2b5a1a961 100644 --- a/src/openai/types/responses/response_item_list.py +++ b/src/openai/types/responses/response_item_list.py @@ -10,6 +10,8 @@ class ResponseItemList(BaseModel): + """A list of Response items.""" + data: List[ResponseItem] """A list of items used to generate this response.""" diff --git a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py index 54eff38373..303ef494a3 100644 --- a/src/openai/types/responses/response_mcp_call_arguments_delta_event.py +++ b/src/openai/types/responses/response_mcp_call_arguments_delta_event.py @@ -8,6 +8,10 @@ class ResponseMcpCallArgumentsDeltaEvent(BaseModel): + """ + Emitted when there is a delta (partial update) to the arguments of an MCP tool call. + """ + delta: str """ A JSON string containing the partial update to the arguments for the MCP tool diff --git a/src/openai/types/responses/response_mcp_call_arguments_done_event.py b/src/openai/types/responses/response_mcp_call_arguments_done_event.py index 59ce9bc944..59e71be77c 100644 --- a/src/openai/types/responses/response_mcp_call_arguments_done_event.py +++ b/src/openai/types/responses/response_mcp_call_arguments_done_event.py @@ -8,6 +8,8 @@ class ResponseMcpCallArgumentsDoneEvent(BaseModel): + """Emitted when the arguments for an MCP tool call are finalized.""" + arguments: str """A JSON string containing the finalized arguments for the MCP tool call.""" diff --git a/src/openai/types/responses/response_mcp_call_completed_event.py b/src/openai/types/responses/response_mcp_call_completed_event.py index 2fee5dff81..bee54d4039 100644 --- a/src/openai/types/responses/response_mcp_call_completed_event.py +++ b/src/openai/types/responses/response_mcp_call_completed_event.py @@ -8,6 +8,8 @@ class ResponseMcpCallCompletedEvent(BaseModel): + """Emitted when an MCP tool call has completed successfully.""" + item_id: str """The ID of the MCP tool call item that completed.""" diff --git a/src/openai/types/responses/response_mcp_call_failed_event.py b/src/openai/types/responses/response_mcp_call_failed_event.py index ca41ab7159..cb3130b155 100644 --- a/src/openai/types/responses/response_mcp_call_failed_event.py +++ b/src/openai/types/responses/response_mcp_call_failed_event.py @@ -8,6 +8,8 @@ class ResponseMcpCallFailedEvent(BaseModel): + """Emitted when an MCP tool call has failed.""" + item_id: str """The ID of the MCP tool call item that failed.""" diff --git a/src/openai/types/responses/response_mcp_call_in_progress_event.py b/src/openai/types/responses/response_mcp_call_in_progress_event.py index 401c316851..7cf6a1decf 100644 --- a/src/openai/types/responses/response_mcp_call_in_progress_event.py +++ b/src/openai/types/responses/response_mcp_call_in_progress_event.py @@ -8,6 +8,8 @@ class ResponseMcpCallInProgressEvent(BaseModel): + """Emitted when an MCP tool call is in progress.""" + item_id: str """The unique identifier of the MCP tool call item being processed.""" diff --git a/src/openai/types/responses/response_mcp_list_tools_completed_event.py b/src/openai/types/responses/response_mcp_list_tools_completed_event.py index c60ad88ee5..685ba59c4d 100644 --- a/src/openai/types/responses/response_mcp_list_tools_completed_event.py +++ b/src/openai/types/responses/response_mcp_list_tools_completed_event.py @@ -8,6 +8,8 @@ class ResponseMcpListToolsCompletedEvent(BaseModel): + """Emitted when the list of available MCP tools has been successfully retrieved.""" + item_id: str """The ID of the MCP tool call item that produced this output.""" diff --git a/src/openai/types/responses/response_mcp_list_tools_failed_event.py b/src/openai/types/responses/response_mcp_list_tools_failed_event.py index 0c966c447a..c5fa54d231 100644 --- a/src/openai/types/responses/response_mcp_list_tools_failed_event.py +++ b/src/openai/types/responses/response_mcp_list_tools_failed_event.py @@ -8,6 +8,8 @@ class ResponseMcpListToolsFailedEvent(BaseModel): + """Emitted when the attempt to list available MCP tools has failed.""" + item_id: str """The ID of the MCP tool call item that failed.""" diff --git a/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py b/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py index f451db1ed5..403fdbdeb3 100644 --- a/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py +++ b/src/openai/types/responses/response_mcp_list_tools_in_progress_event.py @@ -8,6 +8,10 @@ class ResponseMcpListToolsInProgressEvent(BaseModel): + """ + Emitted when the system is in the process of retrieving the list of available MCP tools. + """ + item_id: str """The ID of the MCP tool call item that is being processed.""" diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index f0a66e1836..990f947b90 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -32,6 +32,8 @@ class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + id: str """The unique ID of the image generation call.""" @@ -46,6 +48,8 @@ class ImageGenerationCall(BaseModel): class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + command: List[str] """The command to run.""" @@ -66,6 +70,8 @@ class LocalShellCallAction(BaseModel): class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + id: str """The unique ID of the local shell call.""" @@ -83,6 +89,8 @@ class LocalShellCall(BaseModel): class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + id: str """The unique ID of the tool call.""" @@ -119,6 +127,8 @@ class McpCall(BaseModel): class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + input_schema: object """The JSON schema describing the tool's input.""" @@ -133,6 +143,8 @@ class McpListToolsTool(BaseModel): class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + id: str """The unique ID of the list.""" @@ -150,6 +162,8 @@ class McpListTools(BaseModel): class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + id: str """The unique ID of the approval request.""" diff --git a/src/openai/types/responses/response_output_item_added_event.py b/src/openai/types/responses/response_output_item_added_event.py index 7cd2a3946d..a42f6281e3 100644 --- a/src/openai/types/responses/response_output_item_added_event.py +++ b/src/openai/types/responses/response_output_item_added_event.py @@ -9,6 +9,8 @@ class ResponseOutputItemAddedEvent(BaseModel): + """Emitted when a new output item is added.""" + item: ResponseOutputItem """The output item that was added.""" diff --git a/src/openai/types/responses/response_output_item_done_event.py b/src/openai/types/responses/response_output_item_done_event.py index 37d3694cf7..50b99da569 100644 --- a/src/openai/types/responses/response_output_item_done_event.py +++ b/src/openai/types/responses/response_output_item_done_event.py @@ -9,6 +9,8 @@ class ResponseOutputItemDoneEvent(BaseModel): + """Emitted when an output item is marked done.""" + item: ResponseOutputItem """The output item that was marked done.""" diff --git a/src/openai/types/responses/response_output_message.py b/src/openai/types/responses/response_output_message.py index 3864aa2111..9c1d1f97fc 100644 --- a/src/openai/types/responses/response_output_message.py +++ b/src/openai/types/responses/response_output_message.py @@ -14,6 +14,8 @@ class ResponseOutputMessage(BaseModel): + """An output message from the model.""" + id: str """The unique ID of the output message.""" diff --git a/src/openai/types/responses/response_output_message_param.py b/src/openai/types/responses/response_output_message_param.py index 46cbbd20de..9c2f5246a1 100644 --- a/src/openai/types/responses/response_output_message_param.py +++ b/src/openai/types/responses/response_output_message_param.py @@ -14,6 +14,8 @@ class ResponseOutputMessageParam(TypedDict, total=False): + """An output message from the model.""" + id: Required[str] """The unique ID of the output message.""" diff --git a/src/openai/types/responses/response_output_refusal.py b/src/openai/types/responses/response_output_refusal.py index 685c8722a6..6bce26af74 100644 --- a/src/openai/types/responses/response_output_refusal.py +++ b/src/openai/types/responses/response_output_refusal.py @@ -8,6 +8,8 @@ class ResponseOutputRefusal(BaseModel): + """A refusal from the model.""" + refusal: str """The refusal explanation from the model.""" diff --git a/src/openai/types/responses/response_output_refusal_param.py b/src/openai/types/responses/response_output_refusal_param.py index 54cfaf0791..02bdfdcf4f 100644 --- a/src/openai/types/responses/response_output_refusal_param.py +++ b/src/openai/types/responses/response_output_refusal_param.py @@ -8,6 +8,8 @@ class ResponseOutputRefusalParam(TypedDict, total=False): + """A refusal from the model.""" + refusal: Required[str] """The refusal explanation from the model.""" diff --git a/src/openai/types/responses/response_output_text.py b/src/openai/types/responses/response_output_text.py index aa97b629f0..2386fcb3c0 100644 --- a/src/openai/types/responses/response_output_text.py +++ b/src/openai/types/responses/response_output_text.py @@ -19,6 +19,8 @@ class AnnotationFileCitation(BaseModel): + """A citation to a file.""" + file_id: str """The ID of the file.""" @@ -33,6 +35,8 @@ class AnnotationFileCitation(BaseModel): class AnnotationURLCitation(BaseModel): + """A citation for a web resource used to generate a model response.""" + end_index: int """The index of the last character of the URL citation in the message.""" @@ -50,6 +54,8 @@ class AnnotationURLCitation(BaseModel): class AnnotationContainerFileCitation(BaseModel): + """A citation for a container file used to generate a model response.""" + container_id: str """The ID of the container file.""" @@ -70,6 +76,8 @@ class AnnotationContainerFileCitation(BaseModel): class AnnotationFilePath(BaseModel): + """A path to a file.""" + file_id: str """The ID of the file.""" @@ -87,6 +95,8 @@ class AnnotationFilePath(BaseModel): class LogprobTopLogprob(BaseModel): + """The top log probability of a token.""" + token: str bytes: List[int] @@ -95,6 +105,8 @@ class LogprobTopLogprob(BaseModel): class Logprob(BaseModel): + """The log probability of a token.""" + token: str bytes: List[int] @@ -105,6 +117,8 @@ class Logprob(BaseModel): class ResponseOutputText(BaseModel): + """A text output from the model.""" + annotations: List[Annotation] """The annotations of the text output.""" diff --git a/src/openai/types/responses/response_output_text_annotation_added_event.py b/src/openai/types/responses/response_output_text_annotation_added_event.py index 62d8f72863..b9dc262150 100644 --- a/src/openai/types/responses/response_output_text_annotation_added_event.py +++ b/src/openai/types/responses/response_output_text_annotation_added_event.py @@ -8,6 +8,8 @@ class ResponseOutputTextAnnotationAddedEvent(BaseModel): + """Emitted when an annotation is added to output text content.""" + annotation: object """The annotation object being added. (See annotation schema for details.)""" diff --git a/src/openai/types/responses/response_output_text_param.py b/src/openai/types/responses/response_output_text_param.py index 63d2d394a8..bc30fbcd8e 100644 --- a/src/openai/types/responses/response_output_text_param.py +++ b/src/openai/types/responses/response_output_text_param.py @@ -18,6 +18,8 @@ class AnnotationFileCitation(TypedDict, total=False): + """A citation to a file.""" + file_id: Required[str] """The ID of the file.""" @@ -32,6 +34,8 @@ class AnnotationFileCitation(TypedDict, total=False): class AnnotationURLCitation(TypedDict, total=False): + """A citation for a web resource used to generate a model response.""" + end_index: Required[int] """The index of the last character of the URL citation in the message.""" @@ -49,6 +53,8 @@ class AnnotationURLCitation(TypedDict, total=False): class AnnotationContainerFileCitation(TypedDict, total=False): + """A citation for a container file used to generate a model response.""" + container_id: Required[str] """The ID of the container file.""" @@ -69,6 +75,8 @@ class AnnotationContainerFileCitation(TypedDict, total=False): class AnnotationFilePath(TypedDict, total=False): + """A path to a file.""" + file_id: Required[str] """The ID of the file.""" @@ -85,6 +93,8 @@ class AnnotationFilePath(TypedDict, total=False): class LogprobTopLogprob(TypedDict, total=False): + """The top log probability of a token.""" + token: Required[str] bytes: Required[Iterable[int]] @@ -93,6 +103,8 @@ class LogprobTopLogprob(TypedDict, total=False): class Logprob(TypedDict, total=False): + """The log probability of a token.""" + token: Required[str] bytes: Required[Iterable[int]] @@ -103,6 +115,8 @@ class Logprob(TypedDict, total=False): class ResponseOutputTextParam(TypedDict, total=False): + """A text output from the model.""" + annotations: Required[Iterable[Annotation]] """The annotations of the text output.""" diff --git a/src/openai/types/responses/response_prompt.py b/src/openai/types/responses/response_prompt.py index 537c2f8fbc..e3acacf63a 100644 --- a/src/openai/types/responses/response_prompt.py +++ b/src/openai/types/responses/response_prompt.py @@ -14,6 +14,11 @@ class ResponsePrompt(BaseModel): + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + id: str """The unique identifier of the prompt template to use.""" diff --git a/src/openai/types/responses/response_prompt_param.py b/src/openai/types/responses/response_prompt_param.py index d935fa5191..f9a28b62a2 100644 --- a/src/openai/types/responses/response_prompt_param.py +++ b/src/openai/types/responses/response_prompt_param.py @@ -15,6 +15,11 @@ class ResponsePromptParam(TypedDict, total=False): + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + id: Required[str] """The unique identifier of the prompt template to use.""" diff --git a/src/openai/types/responses/response_queued_event.py b/src/openai/types/responses/response_queued_event.py index 40257408a4..a554215275 100644 --- a/src/openai/types/responses/response_queued_event.py +++ b/src/openai/types/responses/response_queued_event.py @@ -9,6 +9,8 @@ class ResponseQueuedEvent(BaseModel): + """Emitted when a response is queued and waiting to be processed.""" + response: Response """The full response object that is queued.""" diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py index fc582cf7c5..1a22eb60cc 100644 --- a/src/openai/types/responses/response_reasoning_item.py +++ b/src/openai/types/responses/response_reasoning_item.py @@ -9,6 +9,8 @@ class Summary(BaseModel): + """A summary text from the model.""" + text: str """A summary of the reasoning output from the model so far.""" @@ -17,6 +19,8 @@ class Summary(BaseModel): class Content(BaseModel): + """Reasoning text from the model.""" + text: str """The reasoning text from the model.""" @@ -25,6 +29,13 @@ class Content(BaseModel): class ResponseReasoningItem(BaseModel): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://platform.openai.com/docs/guides/conversation-state). + """ + id: str """The unique identifier of the reasoning content.""" diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py index 56e88ba28d..40320b72e1 100644 --- a/src/openai/types/responses/response_reasoning_item_param.py +++ b/src/openai/types/responses/response_reasoning_item_param.py @@ -9,6 +9,8 @@ class Summary(TypedDict, total=False): + """A summary text from the model.""" + text: Required[str] """A summary of the reasoning output from the model so far.""" @@ -17,6 +19,8 @@ class Summary(TypedDict, total=False): class Content(TypedDict, total=False): + """Reasoning text from the model.""" + text: Required[str] """The reasoning text from the model.""" @@ -25,6 +29,13 @@ class Content(TypedDict, total=False): class ResponseReasoningItemParam(TypedDict, total=False): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://platform.openai.com/docs/guides/conversation-state). + """ + id: Required[str] """The unique identifier of the reasoning content.""" diff --git a/src/openai/types/responses/response_reasoning_summary_part_added_event.py b/src/openai/types/responses/response_reasoning_summary_part_added_event.py index dc755b253a..e4b0f34231 100644 --- a/src/openai/types/responses/response_reasoning_summary_part_added_event.py +++ b/src/openai/types/responses/response_reasoning_summary_part_added_event.py @@ -8,6 +8,8 @@ class Part(BaseModel): + """The summary part that was added.""" + text: str """The text of the summary part.""" @@ -16,6 +18,8 @@ class Part(BaseModel): class ResponseReasoningSummaryPartAddedEvent(BaseModel): + """Emitted when a new reasoning summary part is added.""" + item_id: str """The ID of the item this summary part is associated with.""" diff --git a/src/openai/types/responses/response_reasoning_summary_part_done_event.py b/src/openai/types/responses/response_reasoning_summary_part_done_event.py index 7cc0b56d66..48f3f684e8 100644 --- a/src/openai/types/responses/response_reasoning_summary_part_done_event.py +++ b/src/openai/types/responses/response_reasoning_summary_part_done_event.py @@ -8,6 +8,8 @@ class Part(BaseModel): + """The completed summary part.""" + text: str """The text of the summary part.""" @@ -16,6 +18,8 @@ class Part(BaseModel): class ResponseReasoningSummaryPartDoneEvent(BaseModel): + """Emitted when a reasoning summary part is completed.""" + item_id: str """The ID of the item this summary part is associated with.""" diff --git a/src/openai/types/responses/response_reasoning_summary_text_delta_event.py b/src/openai/types/responses/response_reasoning_summary_text_delta_event.py index 96652991b6..84bcf039c4 100644 --- a/src/openai/types/responses/response_reasoning_summary_text_delta_event.py +++ b/src/openai/types/responses/response_reasoning_summary_text_delta_event.py @@ -8,6 +8,8 @@ class ResponseReasoningSummaryTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning summary text.""" + delta: str """The text delta that was added to the summary.""" diff --git a/src/openai/types/responses/response_reasoning_summary_text_done_event.py b/src/openai/types/responses/response_reasoning_summary_text_done_event.py index b35b82316a..244d001b75 100644 --- a/src/openai/types/responses/response_reasoning_summary_text_done_event.py +++ b/src/openai/types/responses/response_reasoning_summary_text_done_event.py @@ -8,6 +8,8 @@ class ResponseReasoningSummaryTextDoneEvent(BaseModel): + """Emitted when a reasoning summary text is completed.""" + item_id: str """The ID of the item this summary text is associated with.""" diff --git a/src/openai/types/responses/response_reasoning_text_delta_event.py b/src/openai/types/responses/response_reasoning_text_delta_event.py index e1df893bac..0e05226c94 100644 --- a/src/openai/types/responses/response_reasoning_text_delta_event.py +++ b/src/openai/types/responses/response_reasoning_text_delta_event.py @@ -8,6 +8,8 @@ class ResponseReasoningTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning text.""" + content_index: int """The index of the reasoning content part this delta is associated with.""" diff --git a/src/openai/types/responses/response_reasoning_text_done_event.py b/src/openai/types/responses/response_reasoning_text_done_event.py index d22d984e47..40e3f4701c 100644 --- a/src/openai/types/responses/response_reasoning_text_done_event.py +++ b/src/openai/types/responses/response_reasoning_text_done_event.py @@ -8,6 +8,8 @@ class ResponseReasoningTextDoneEvent(BaseModel): + """Emitted when a reasoning text is completed.""" + content_index: int """The index of the reasoning content part.""" diff --git a/src/openai/types/responses/response_refusal_delta_event.py b/src/openai/types/responses/response_refusal_delta_event.py index 03c903ed28..e3933b7dda 100644 --- a/src/openai/types/responses/response_refusal_delta_event.py +++ b/src/openai/types/responses/response_refusal_delta_event.py @@ -8,6 +8,8 @@ class ResponseRefusalDeltaEvent(BaseModel): + """Emitted when there is a partial refusal text.""" + content_index: int """The index of the content part that the refusal text is added to.""" diff --git a/src/openai/types/responses/response_refusal_done_event.py b/src/openai/types/responses/response_refusal_done_event.py index 61fd51aab0..91adeb6331 100644 --- a/src/openai/types/responses/response_refusal_done_event.py +++ b/src/openai/types/responses/response_refusal_done_event.py @@ -8,6 +8,8 @@ class ResponseRefusalDoneEvent(BaseModel): + """Emitted when refusal text is finalized.""" + content_index: int """The index of the content part that the refusal text is finalized.""" diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py index c53546da6d..fbf4da0b03 100644 --- a/src/openai/types/responses/response_text_config.py +++ b/src/openai/types/responses/response_text_config.py @@ -10,6 +10,14 @@ class ResponseTextConfig(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: Optional[ResponseFormatTextConfig] = None """An object specifying the format that the model must output. diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py index 1229fce35b..9cd54765b0 100644 --- a/src/openai/types/responses/response_text_config_param.py +++ b/src/openai/types/responses/response_text_config_param.py @@ -11,6 +11,14 @@ class ResponseTextConfigParam(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + format: ResponseFormatTextConfigParam """An object specifying the format that the model must output. diff --git a/src/openai/types/responses/response_text_delta_event.py b/src/openai/types/responses/response_text_delta_event.py index b5379b7ac3..4f802abfd2 100644 --- a/src/openai/types/responses/response_text_delta_event.py +++ b/src/openai/types/responses/response_text_delta_event.py @@ -17,6 +17,12 @@ class LogprobTopLogprob(BaseModel): class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + token: str """A possible text token.""" @@ -28,6 +34,8 @@ class Logprob(BaseModel): class ResponseTextDeltaEvent(BaseModel): + """Emitted when there is an additional text delta.""" + content_index: int """The index of the content part that the text delta was added to.""" diff --git a/src/openai/types/responses/response_text_done_event.py b/src/openai/types/responses/response_text_done_event.py index d9776a1844..75bd479870 100644 --- a/src/openai/types/responses/response_text_done_event.py +++ b/src/openai/types/responses/response_text_done_event.py @@ -17,6 +17,12 @@ class LogprobTopLogprob(BaseModel): class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + token: str """A possible text token.""" @@ -28,6 +34,8 @@ class Logprob(BaseModel): class ResponseTextDoneEvent(BaseModel): + """Emitted when text content is finalized.""" + content_index: int """The index of the content part that the text content is finalized.""" diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py index 52b93ac578..d4b739c598 100644 --- a/src/openai/types/responses/response_usage.py +++ b/src/openai/types/responses/response_usage.py @@ -6,6 +6,8 @@ class InputTokensDetails(BaseModel): + """A detailed breakdown of the input tokens.""" + cached_tokens: int """The number of tokens that were retrieved from the cache. @@ -14,11 +16,18 @@ class InputTokensDetails(BaseModel): class OutputTokensDetails(BaseModel): + """A detailed breakdown of the output tokens.""" + reasoning_tokens: int """The number of reasoning tokens.""" class ResponseUsage(BaseModel): + """ + Represents token usage details including input tokens, output tokens, + a breakdown of output tokens, and the total tokens used. + """ + input_tokens: int """The number of input tokens.""" diff --git a/src/openai/types/responses/response_web_search_call_completed_event.py b/src/openai/types/responses/response_web_search_call_completed_event.py index 497f7bfe35..5aa7afe609 100644 --- a/src/openai/types/responses/response_web_search_call_completed_event.py +++ b/src/openai/types/responses/response_web_search_call_completed_event.py @@ -8,6 +8,8 @@ class ResponseWebSearchCallCompletedEvent(BaseModel): + """Emitted when a web search call is completed.""" + item_id: str """Unique ID for the output item associated with the web search call.""" diff --git a/src/openai/types/responses/response_web_search_call_in_progress_event.py b/src/openai/types/responses/response_web_search_call_in_progress_event.py index da8b3fe404..73b30ff5c0 100644 --- a/src/openai/types/responses/response_web_search_call_in_progress_event.py +++ b/src/openai/types/responses/response_web_search_call_in_progress_event.py @@ -8,6 +8,8 @@ class ResponseWebSearchCallInProgressEvent(BaseModel): + """Emitted when a web search call is initiated.""" + item_id: str """Unique ID for the output item associated with the web search call.""" diff --git a/src/openai/types/responses/response_web_search_call_searching_event.py b/src/openai/types/responses/response_web_search_call_searching_event.py index 42df9cb298..959c095187 100644 --- a/src/openai/types/responses/response_web_search_call_searching_event.py +++ b/src/openai/types/responses/response_web_search_call_searching_event.py @@ -8,6 +8,8 @@ class ResponseWebSearchCallSearchingEvent(BaseModel): + """Emitted when a web search call is executing.""" + item_id: str """Unique ID for the output item associated with the web search call.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index bb32d4e1ec..1f1ef12358 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -38,6 +38,8 @@ class McpAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -54,6 +56,8 @@ class McpAllowedToolsMcpToolFilter(BaseModel): class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -67,6 +71,8 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + read_only: Optional[bool] = None """Indicates whether or not a tool modifies data or is read-only. @@ -80,6 +86,13 @@ class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): class McpRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None """A filter object to specify which tools are allowed.""" @@ -91,6 +104,11 @@ class McpRequireApprovalMcpToolApprovalFilter(BaseModel): class Mcp(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: str """A label for this MCP server, used to identify it in tool calls.""" @@ -157,6 +175,11 @@ class Mcp(BaseModel): class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + type: Literal["auto"] """Always `auto`.""" @@ -170,6 +193,8 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): class CodeInterpreter(BaseModel): + """A tool that runs Python code to help generate a response to a prompt.""" + container: CodeInterpreterContainer """The code interpreter container. @@ -182,6 +207,12 @@ class CodeInterpreter(BaseModel): class ImageGenerationInputImageMask(BaseModel): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + file_id: Optional[str] = None """File ID for the mask image.""" @@ -190,6 +221,8 @@ class ImageGenerationInputImageMask(BaseModel): class ImageGeneration(BaseModel): + """A tool that generates images using a model like `gpt-image-1`.""" + type: Literal["image_generation"] """The type of the image generation tool. Always `image_generation`.""" @@ -248,6 +281,8 @@ class ImageGeneration(BaseModel): class LocalShell(BaseModel): + """A tool that allows the model to execute shell commands in a local environment.""" + type: Literal["local_shell"] """The type of the local shell tool. Always `local_shell`.""" diff --git a/src/openai/types/responses/tool_choice_allowed.py b/src/openai/types/responses/tool_choice_allowed.py index d7921dcb2a..400e170a57 100644 --- a/src/openai/types/responses/tool_choice_allowed.py +++ b/src/openai/types/responses/tool_choice_allowed.py @@ -9,6 +9,8 @@ class ToolChoiceAllowed(BaseModel): + """Constrains the tools available to the model to a pre-defined set.""" + mode: Literal["auto", "required"] """Constrains the tools available to the model to a pre-defined set. diff --git a/src/openai/types/responses/tool_choice_allowed_param.py b/src/openai/types/responses/tool_choice_allowed_param.py index 0712cab43b..cb316c1560 100644 --- a/src/openai/types/responses/tool_choice_allowed_param.py +++ b/src/openai/types/responses/tool_choice_allowed_param.py @@ -9,6 +9,8 @@ class ToolChoiceAllowedParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + mode: Required[Literal["auto", "required"]] """Constrains the tools available to the model to a pre-defined set. diff --git a/src/openai/types/responses/tool_choice_apply_patch.py b/src/openai/types/responses/tool_choice_apply_patch.py index 7f815aa1a1..ef5a5e8bfa 100644 --- a/src/openai/types/responses/tool_choice_apply_patch.py +++ b/src/openai/types/responses/tool_choice_apply_patch.py @@ -8,5 +8,7 @@ class ToolChoiceApplyPatch(BaseModel): + """Forces the model to call the apply_patch tool when executing a tool call.""" + type: Literal["apply_patch"] """The tool to call. Always `apply_patch`.""" diff --git a/src/openai/types/responses/tool_choice_apply_patch_param.py b/src/openai/types/responses/tool_choice_apply_patch_param.py index 00d4b25f0e..193c99328a 100644 --- a/src/openai/types/responses/tool_choice_apply_patch_param.py +++ b/src/openai/types/responses/tool_choice_apply_patch_param.py @@ -8,5 +8,7 @@ class ToolChoiceApplyPatchParam(TypedDict, total=False): + """Forces the model to call the apply_patch tool when executing a tool call.""" + type: Required[Literal["apply_patch"]] """The tool to call. Always `apply_patch`.""" diff --git a/src/openai/types/responses/tool_choice_custom.py b/src/openai/types/responses/tool_choice_custom.py index d600e53616..dec85ef78c 100644 --- a/src/openai/types/responses/tool_choice_custom.py +++ b/src/openai/types/responses/tool_choice_custom.py @@ -8,6 +8,8 @@ class ToolChoiceCustom(BaseModel): + """Use this option to force the model to call a specific custom tool.""" + name: str """The name of the custom tool to call.""" diff --git a/src/openai/types/responses/tool_choice_custom_param.py b/src/openai/types/responses/tool_choice_custom_param.py index 55bc53b730..ccdbab568a 100644 --- a/src/openai/types/responses/tool_choice_custom_param.py +++ b/src/openai/types/responses/tool_choice_custom_param.py @@ -8,6 +8,8 @@ class ToolChoiceCustomParam(TypedDict, total=False): + """Use this option to force the model to call a specific custom tool.""" + name: Required[str] """The name of the custom tool to call.""" diff --git a/src/openai/types/responses/tool_choice_function.py b/src/openai/types/responses/tool_choice_function.py index 8d2a4f2822..b2aab24aca 100644 --- a/src/openai/types/responses/tool_choice_function.py +++ b/src/openai/types/responses/tool_choice_function.py @@ -8,6 +8,8 @@ class ToolChoiceFunction(BaseModel): + """Use this option to force the model to call a specific function.""" + name: str """The name of the function to call.""" diff --git a/src/openai/types/responses/tool_choice_function_param.py b/src/openai/types/responses/tool_choice_function_param.py index 910537fd97..837465ebd7 100644 --- a/src/openai/types/responses/tool_choice_function_param.py +++ b/src/openai/types/responses/tool_choice_function_param.py @@ -8,6 +8,8 @@ class ToolChoiceFunctionParam(TypedDict, total=False): + """Use this option to force the model to call a specific function.""" + name: Required[str] """The name of the function to call.""" diff --git a/src/openai/types/responses/tool_choice_mcp.py b/src/openai/types/responses/tool_choice_mcp.py index 8763d81635..a2c8049c2d 100644 --- a/src/openai/types/responses/tool_choice_mcp.py +++ b/src/openai/types/responses/tool_choice_mcp.py @@ -9,6 +9,10 @@ class ToolChoiceMcp(BaseModel): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + server_label: str """The label of the MCP server to use.""" diff --git a/src/openai/types/responses/tool_choice_mcp_param.py b/src/openai/types/responses/tool_choice_mcp_param.py index afcceb8cc5..9726e47a47 100644 --- a/src/openai/types/responses/tool_choice_mcp_param.py +++ b/src/openai/types/responses/tool_choice_mcp_param.py @@ -9,6 +9,10 @@ class ToolChoiceMcpParam(TypedDict, total=False): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + server_label: Required[str] """The label of the MCP server to use.""" diff --git a/src/openai/types/responses/tool_choice_shell.py b/src/openai/types/responses/tool_choice_shell.py index 1ad21c58f3..a78eccc387 100644 --- a/src/openai/types/responses/tool_choice_shell.py +++ b/src/openai/types/responses/tool_choice_shell.py @@ -8,5 +8,7 @@ class ToolChoiceShell(BaseModel): + """Forces the model to call the shell tool when a tool call is required.""" + type: Literal["shell"] """The tool to call. Always `shell`.""" diff --git a/src/openai/types/responses/tool_choice_shell_param.py b/src/openai/types/responses/tool_choice_shell_param.py index 2b04c00d56..0dbcc90f39 100644 --- a/src/openai/types/responses/tool_choice_shell_param.py +++ b/src/openai/types/responses/tool_choice_shell_param.py @@ -8,5 +8,7 @@ class ToolChoiceShellParam(TypedDict, total=False): + """Forces the model to call the shell tool when a tool call is required.""" + type: Required[Literal["shell"]] """The tool to call. Always `shell`.""" diff --git a/src/openai/types/responses/tool_choice_types.py b/src/openai/types/responses/tool_choice_types.py index b31a826051..044c014b19 100644 --- a/src/openai/types/responses/tool_choice_types.py +++ b/src/openai/types/responses/tool_choice_types.py @@ -8,6 +8,11 @@ class ToolChoiceTypes(BaseModel): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools). + """ + type: Literal[ "file_search", "web_search_preview", diff --git a/src/openai/types/responses/tool_choice_types_param.py b/src/openai/types/responses/tool_choice_types_param.py index 15e0357471..9bf02dbfcc 100644 --- a/src/openai/types/responses/tool_choice_types_param.py +++ b/src/openai/types/responses/tool_choice_types_param.py @@ -8,6 +8,11 @@ class ToolChoiceTypesParam(TypedDict, total=False): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools). + """ + type: Required[ Literal[ "file_search", diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 779acf0a53..c6ffa39192 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -40,6 +40,8 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -56,6 +58,8 @@ class McpAllowedToolsMcpToolFilter(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -69,6 +73,8 @@ class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + read_only: bool """Indicates whether or not a tool modifies data or is read-only. @@ -82,6 +88,13 @@ class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + always: McpRequireApprovalMcpToolApprovalFilterAlways """A filter object to specify which tools are allowed.""" @@ -93,6 +106,11 @@ class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + server_label: Required[str] """A label for this MCP server, used to identify it in tool calls.""" @@ -157,6 +175,11 @@ class Mcp(TypedDict, total=False): class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + type: Required[Literal["auto"]] """Always `auto`.""" @@ -170,6 +193,8 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): class CodeInterpreter(TypedDict, total=False): + """A tool that runs Python code to help generate a response to a prompt.""" + container: Required[CodeInterpreterContainer] """The code interpreter container. @@ -182,6 +207,12 @@ class CodeInterpreter(TypedDict, total=False): class ImageGenerationInputImageMask(TypedDict, total=False): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + file_id: str """File ID for the mask image.""" @@ -190,6 +221,8 @@ class ImageGenerationInputImageMask(TypedDict, total=False): class ImageGeneration(TypedDict, total=False): + """A tool that generates images using a model like `gpt-image-1`.""" + type: Required[Literal["image_generation"]] """The type of the image generation tool. Always `image_generation`.""" @@ -248,6 +281,8 @@ class ImageGeneration(TypedDict, total=False): class LocalShell(TypedDict, total=False): + """A tool that allows the model to execute shell commands in a local environment.""" + type: Required[Literal["local_shell"]] """The type of the local shell tool. Always `local_shell`.""" diff --git a/src/openai/types/responses/web_search_preview_tool.py b/src/openai/types/responses/web_search_preview_tool.py index 66d6a24679..12478e896d 100644 --- a/src/openai/types/responses/web_search_preview_tool.py +++ b/src/openai/types/responses/web_search_preview_tool.py @@ -9,6 +9,8 @@ class UserLocation(BaseModel): + """The user's location.""" + type: Literal["approximate"] """The type of location approximation. Always `approximate`.""" @@ -32,6 +34,11 @@ class UserLocation(BaseModel): class WebSearchPreviewTool(BaseModel): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + type: Literal["web_search_preview", "web_search_preview_2025_03_11"] """The type of the web search tool. diff --git a/src/openai/types/responses/web_search_preview_tool_param.py b/src/openai/types/responses/web_search_preview_tool_param.py index ec2173f8e8..09619a3394 100644 --- a/src/openai/types/responses/web_search_preview_tool_param.py +++ b/src/openai/types/responses/web_search_preview_tool_param.py @@ -9,6 +9,8 @@ class UserLocation(TypedDict, total=False): + """The user's location.""" + type: Required[Literal["approximate"]] """The type of location approximation. Always `approximate`.""" @@ -32,6 +34,11 @@ class UserLocation(TypedDict, total=False): class WebSearchPreviewToolParam(TypedDict, total=False): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] """The type of the web search tool. diff --git a/src/openai/types/responses/web_search_tool.py b/src/openai/types/responses/web_search_tool.py index bde9600c87..769f5c93a4 100644 --- a/src/openai/types/responses/web_search_tool.py +++ b/src/openai/types/responses/web_search_tool.py @@ -9,6 +9,8 @@ class Filters(BaseModel): + """Filters for the search.""" + allowed_domains: Optional[List[str]] = None """Allowed domains for the search. @@ -20,6 +22,8 @@ class Filters(BaseModel): class UserLocation(BaseModel): + """The approximate location of the user.""" + city: Optional[str] = None """Free text input for the city of the user, e.g. `San Francisco`.""" @@ -43,6 +47,12 @@ class UserLocation(BaseModel): class WebSearchTool(BaseModel): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + type: Literal["web_search", "web_search_2025_08_26"] """The type of the web search tool. diff --git a/src/openai/types/responses/web_search_tool_param.py b/src/openai/types/responses/web_search_tool_param.py index 7fa19e9c23..a4531a9304 100644 --- a/src/openai/types/responses/web_search_tool_param.py +++ b/src/openai/types/responses/web_search_tool_param.py @@ -11,6 +11,8 @@ class Filters(TypedDict, total=False): + """Filters for the search.""" + allowed_domains: Optional[SequenceNotStr[str]] """Allowed domains for the search. @@ -22,6 +24,8 @@ class Filters(TypedDict, total=False): class UserLocation(TypedDict, total=False): + """The approximate location of the user.""" + city: Optional[str] """Free text input for the city of the user, e.g. `San Francisco`.""" @@ -45,6 +49,12 @@ class UserLocation(TypedDict, total=False): class WebSearchToolParam(TypedDict, total=False): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + type: Required[Literal["web_search", "web_search_2025_08_26"]] """The type of the web search tool. diff --git a/src/openai/types/shared/comparison_filter.py b/src/openai/types/shared/comparison_filter.py index 33415ca4f9..852cac1738 100644 --- a/src/openai/types/shared/comparison_filter.py +++ b/src/openai/types/shared/comparison_filter.py @@ -9,6 +9,10 @@ class ComparisonFilter(BaseModel): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + key: str """The key to compare against the value.""" diff --git a/src/openai/types/shared/compound_filter.py b/src/openai/types/shared/compound_filter.py index 3aefa43647..4801aaac1a 100644 --- a/src/openai/types/shared/compound_filter.py +++ b/src/openai/types/shared/compound_filter.py @@ -12,6 +12,8 @@ class CompoundFilter(BaseModel): + """Combine multiple filters using `and` or `or`.""" + filters: List[Filter] """Array of filters to combine. diff --git a/src/openai/types/shared/custom_tool_input_format.py b/src/openai/types/shared/custom_tool_input_format.py index 53c8323ed2..9391692b7b 100644 --- a/src/openai/types/shared/custom_tool_input_format.py +++ b/src/openai/types/shared/custom_tool_input_format.py @@ -10,11 +10,15 @@ class Text(BaseModel): + """Unconstrained free-form text.""" + type: Literal["text"] """Unconstrained text format. Always `text`.""" class Grammar(BaseModel): + """A grammar defined by the user.""" + definition: str """The grammar definition.""" diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index b19476bcb5..1220a045b1 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -10,6 +10,12 @@ class Reasoning(BaseModel): + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + effort: Optional[ReasoningEffort] = None """ Constrains effort on reasoning for diff --git a/src/openai/types/shared/response_format_json_object.py b/src/openai/types/shared/response_format_json_object.py index 2aaa5dbdfe..98e0da6a2c 100644 --- a/src/openai/types/shared/response_format_json_object.py +++ b/src/openai/types/shared/response_format_json_object.py @@ -8,5 +8,13 @@ class ResponseFormatJSONObject(BaseModel): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + type: Literal["json_object"] """The type of response format being defined. Always `json_object`.""" diff --git a/src/openai/types/shared/response_format_json_schema.py b/src/openai/types/shared/response_format_json_schema.py index c7924446f4..9b2adb66cd 100644 --- a/src/openai/types/shared/response_format_json_schema.py +++ b/src/openai/types/shared/response_format_json_schema.py @@ -11,6 +11,8 @@ class JSONSchema(BaseModel): + """Structured Outputs configuration options, including a JSON Schema.""" + name: str """The name of the response format. @@ -41,6 +43,12 @@ class JSONSchema(BaseModel): class ResponseFormatJSONSchema(BaseModel): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs). + """ + json_schema: JSONSchema """Structured Outputs configuration options, including a JSON Schema.""" diff --git a/src/openai/types/shared/response_format_text.py b/src/openai/types/shared/response_format_text.py index f0c8cfb700..9f4bc0d13e 100644 --- a/src/openai/types/shared/response_format_text.py +++ b/src/openai/types/shared/response_format_text.py @@ -8,5 +8,7 @@ class ResponseFormatText(BaseModel): + """Default response format. Used to generate text responses.""" + type: Literal["text"] """The type of response format being defined. Always `text`.""" diff --git a/src/openai/types/shared/response_format_text_grammar.py b/src/openai/types/shared/response_format_text_grammar.py index b02f99c1b8..84cd141278 100644 --- a/src/openai/types/shared/response_format_text_grammar.py +++ b/src/openai/types/shared/response_format_text_grammar.py @@ -8,6 +8,11 @@ class ResponseFormatTextGrammar(BaseModel): + """ + A custom grammar for the model to follow when generating text. + Learn more in the [custom grammars guide](https://platform.openai.com/docs/guides/custom-grammars). + """ + grammar: str """The custom grammar for the model to follow.""" diff --git a/src/openai/types/shared/response_format_text_python.py b/src/openai/types/shared/response_format_text_python.py index 4cd18d46fa..1b04cb62ba 100644 --- a/src/openai/types/shared/response_format_text_python.py +++ b/src/openai/types/shared/response_format_text_python.py @@ -8,5 +8,11 @@ class ResponseFormatTextPython(BaseModel): + """Configure the model to generate valid Python code. + + See the + [custom grammars guide](https://platform.openai.com/docs/guides/custom-grammars) for more details. + """ + type: Literal["python"] """The type of response format being defined. Always `python`.""" diff --git a/src/openai/types/shared_params/comparison_filter.py b/src/openai/types/shared_params/comparison_filter.py index 1c40729c19..363688e467 100644 --- a/src/openai/types/shared_params/comparison_filter.py +++ b/src/openai/types/shared_params/comparison_filter.py @@ -11,6 +11,10 @@ class ComparisonFilter(TypedDict, total=False): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + key: Required[str] """The key to compare against the value.""" diff --git a/src/openai/types/shared_params/compound_filter.py b/src/openai/types/shared_params/compound_filter.py index d12e9b1bda..9358e46083 100644 --- a/src/openai/types/shared_params/compound_filter.py +++ b/src/openai/types/shared_params/compound_filter.py @@ -13,6 +13,8 @@ class CompoundFilter(TypedDict, total=False): + """Combine multiple filters using `and` or `or`.""" + filters: Required[Iterable[Filter]] """Array of filters to combine. diff --git a/src/openai/types/shared_params/custom_tool_input_format.py b/src/openai/types/shared_params/custom_tool_input_format.py index 37df393e39..ddc71cacb4 100644 --- a/src/openai/types/shared_params/custom_tool_input_format.py +++ b/src/openai/types/shared_params/custom_tool_input_format.py @@ -9,11 +9,15 @@ class Text(TypedDict, total=False): + """Unconstrained free-form text.""" + type: Required[Literal["text"]] """Unconstrained text format. Always `text`.""" class Grammar(TypedDict, total=False): + """A grammar defined by the user.""" + definition: Required[str] """The grammar definition.""" diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 71cb37c65e..68d9c374b8 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -11,6 +11,12 @@ class Reasoning(TypedDict, total=False): + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + effort: Optional[ReasoningEffort] """ Constrains effort on reasoning for diff --git a/src/openai/types/shared_params/response_format_json_object.py b/src/openai/types/shared_params/response_format_json_object.py index d4d1deaae5..ef5d43be2e 100644 --- a/src/openai/types/shared_params/response_format_json_object.py +++ b/src/openai/types/shared_params/response_format_json_object.py @@ -8,5 +8,13 @@ class ResponseFormatJSONObject(TypedDict, total=False): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + type: Required[Literal["json_object"]] """The type of response format being defined. Always `json_object`.""" diff --git a/src/openai/types/shared_params/response_format_json_schema.py b/src/openai/types/shared_params/response_format_json_schema.py index 5b0a13ee06..0a0e846873 100644 --- a/src/openai/types/shared_params/response_format_json_schema.py +++ b/src/openai/types/shared_params/response_format_json_schema.py @@ -9,6 +9,8 @@ class JSONSchema(TypedDict, total=False): + """Structured Outputs configuration options, including a JSON Schema.""" + name: Required[str] """The name of the response format. @@ -39,6 +41,12 @@ class JSONSchema(TypedDict, total=False): class ResponseFormatJSONSchema(TypedDict, total=False): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs). + """ + json_schema: Required[JSONSchema] """Structured Outputs configuration options, including a JSON Schema.""" diff --git a/src/openai/types/shared_params/response_format_text.py b/src/openai/types/shared_params/response_format_text.py index c3ef2b0816..c195036f95 100644 --- a/src/openai/types/shared_params/response_format_text.py +++ b/src/openai/types/shared_params/response_format_text.py @@ -8,5 +8,7 @@ class ResponseFormatText(TypedDict, total=False): + """Default response format. Used to generate text responses.""" + type: Required[Literal["text"]] """The type of response format being defined. Always `text`.""" diff --git a/src/openai/types/static_file_chunking_strategy_object_param.py b/src/openai/types/static_file_chunking_strategy_object_param.py index 0cdf35c0df..40188a41d5 100644 --- a/src/openai/types/static_file_chunking_strategy_object_param.py +++ b/src/openai/types/static_file_chunking_strategy_object_param.py @@ -10,6 +10,8 @@ class StaticFileChunkingStrategyObjectParam(TypedDict, total=False): + """Customize your own chunking strategy by setting chunk size and chunk overlap.""" + static: Required[StaticFileChunkingStrategyParam] type: Required[Literal["static"]] diff --git a/src/openai/types/upload.py b/src/openai/types/upload.py index 914b69a863..d248da6ee3 100644 --- a/src/openai/types/upload.py +++ b/src/openai/types/upload.py @@ -10,6 +10,8 @@ class Upload(BaseModel): + """The Upload object can accept byte chunks in the form of Parts.""" + id: str """The Upload unique identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/upload_create_params.py b/src/openai/types/upload_create_params.py index ab4cded81d..c25d65bedd 100644 --- a/src/openai/types/upload_create_params.py +++ b/src/openai/types/upload_create_params.py @@ -39,6 +39,11 @@ class UploadCreateParams(TypedDict, total=False): class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a file. + + By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted. + """ + anchor: Required[Literal["created_at"]] """Anchor timestamp after which the expiration policy applies. diff --git a/src/openai/types/uploads/upload_part.py b/src/openai/types/uploads/upload_part.py index e09621d8f9..e585b1a227 100644 --- a/src/openai/types/uploads/upload_part.py +++ b/src/openai/types/uploads/upload_part.py @@ -8,6 +8,8 @@ class UploadPart(BaseModel): + """The upload Part represents a chunk of bytes we can add to an Upload object.""" + id: str """The upload Part unique identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/vector_store.py b/src/openai/types/vector_store.py index 2473a442d2..82899ecd1b 100644 --- a/src/openai/types/vector_store.py +++ b/src/openai/types/vector_store.py @@ -27,6 +27,8 @@ class FileCounts(BaseModel): class ExpiresAfter(BaseModel): + """The expiration policy for a vector store.""" + anchor: Literal["last_active_at"] """Anchor timestamp after which the expiration policy applies. @@ -38,6 +40,10 @@ class ExpiresAfter(BaseModel): class VectorStore(BaseModel): + """ + A vector store is a collection of processed files can be used by the `file_search` tool. + """ + id: str """The identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/vector_store_create_params.py b/src/openai/types/vector_store_create_params.py index f373a6ed28..2b72562984 100644 --- a/src/openai/types/vector_store_create_params.py +++ b/src/openai/types/vector_store_create_params.py @@ -51,6 +51,8 @@ class VectorStoreCreateParams(TypedDict, total=False): class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a vector store.""" + anchor: Required[Literal["last_active_at"]] """Anchor timestamp after which the expiration policy applies. diff --git a/src/openai/types/vector_store_search_params.py b/src/openai/types/vector_store_search_params.py index 8b7b13c4a1..851d63c5d1 100644 --- a/src/openai/types/vector_store_search_params.py +++ b/src/openai/types/vector_store_search_params.py @@ -36,6 +36,8 @@ class VectorStoreSearchParams(TypedDict, total=False): class RankingOptions(TypedDict, total=False): + """Ranking options for search.""" + ranker: Literal["none", "auto", "default-2024-11-15"] """Enable re-ranking; set to `none` to disable, which can help reduce latency.""" diff --git a/src/openai/types/vector_store_update_params.py b/src/openai/types/vector_store_update_params.py index 4f6ac63963..7c6f891170 100644 --- a/src/openai/types/vector_store_update_params.py +++ b/src/openai/types/vector_store_update_params.py @@ -29,6 +29,8 @@ class VectorStoreUpdateParams(TypedDict, total=False): class ExpiresAfter(TypedDict, total=False): + """The expiration policy for a vector store.""" + anchor: Required[Literal["last_active_at"]] """Anchor timestamp after which the expiration policy applies. diff --git a/src/openai/types/vector_stores/vector_store_file.py b/src/openai/types/vector_stores/vector_store_file.py index 001584dfd7..c1ea02227f 100644 --- a/src/openai/types/vector_stores/vector_store_file.py +++ b/src/openai/types/vector_stores/vector_store_file.py @@ -10,6 +10,11 @@ class LastError(BaseModel): + """The last error associated with this vector store file. + + Will be `null` if there are no errors. + """ + code: Literal["server_error", "unsupported_file", "invalid_file"] """One of `server_error`, `unsupported_file`, or `invalid_file`.""" @@ -18,6 +23,8 @@ class LastError(BaseModel): class VectorStoreFile(BaseModel): + """A list of files attached to a vector store.""" + id: str """The identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/vector_stores/vector_store_file_batch.py b/src/openai/types/vector_stores/vector_store_file_batch.py index 57dbfbd809..b07eb25da5 100644 --- a/src/openai/types/vector_stores/vector_store_file_batch.py +++ b/src/openai/types/vector_stores/vector_store_file_batch.py @@ -25,6 +25,8 @@ class FileCounts(BaseModel): class VectorStoreFileBatch(BaseModel): + """A batch of files attached to a vector store.""" + id: str """The identifier, which can be referenced in API endpoints.""" diff --git a/src/openai/types/video.py b/src/openai/types/video.py index 22ee3a11f7..e732ea54ec 100644 --- a/src/openai/types/video.py +++ b/src/openai/types/video.py @@ -13,6 +13,8 @@ class Video(BaseModel): + """Structured information describing a generated video job.""" + id: str """Unique identifier for the video job.""" diff --git a/src/openai/types/video_delete_response.py b/src/openai/types/video_delete_response.py index e2673ffe2b..1ed543aec8 100644 --- a/src/openai/types/video_delete_response.py +++ b/src/openai/types/video_delete_response.py @@ -8,6 +8,8 @@ class VideoDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a video.""" + id: str """Identifier of the deleted video.""" diff --git a/src/openai/types/webhooks/batch_cancelled_webhook_event.py b/src/openai/types/webhooks/batch_cancelled_webhook_event.py index 4bbd7307a5..9d1c485f5e 100644 --- a/src/openai/types/webhooks/batch_cancelled_webhook_event.py +++ b/src/openai/types/webhooks/batch_cancelled_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the batch API request.""" class BatchCancelledWebhookEvent(BaseModel): + """Sent when a batch API request has been cancelled.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/batch_completed_webhook_event.py b/src/openai/types/webhooks/batch_completed_webhook_event.py index a47ca156fa..5ae8191789 100644 --- a/src/openai/types/webhooks/batch_completed_webhook_event.py +++ b/src/openai/types/webhooks/batch_completed_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the batch API request.""" class BatchCompletedWebhookEvent(BaseModel): + """Sent when a batch API request has been completed.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/batch_expired_webhook_event.py b/src/openai/types/webhooks/batch_expired_webhook_event.py index e91001e8d8..2f08a7f579 100644 --- a/src/openai/types/webhooks/batch_expired_webhook_event.py +++ b/src/openai/types/webhooks/batch_expired_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the batch API request.""" class BatchExpiredWebhookEvent(BaseModel): + """Sent when a batch API request has expired.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/batch_failed_webhook_event.py b/src/openai/types/webhooks/batch_failed_webhook_event.py index ef80863edb..7166616588 100644 --- a/src/openai/types/webhooks/batch_failed_webhook_event.py +++ b/src/openai/types/webhooks/batch_failed_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the batch API request.""" class BatchFailedWebhookEvent(BaseModel): + """Sent when a batch API request has failed.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/eval_run_canceled_webhook_event.py b/src/openai/types/webhooks/eval_run_canceled_webhook_event.py index 855359f743..1948f8933b 100644 --- a/src/openai/types/webhooks/eval_run_canceled_webhook_event.py +++ b/src/openai/types/webhooks/eval_run_canceled_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the eval run.""" class EvalRunCanceledWebhookEvent(BaseModel): + """Sent when an eval run has been canceled.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/eval_run_failed_webhook_event.py b/src/openai/types/webhooks/eval_run_failed_webhook_event.py index 7671680720..4e4c860abc 100644 --- a/src/openai/types/webhooks/eval_run_failed_webhook_event.py +++ b/src/openai/types/webhooks/eval_run_failed_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the eval run.""" class EvalRunFailedWebhookEvent(BaseModel): + """Sent when an eval run has failed.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py b/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py index d0d1fc2b04..c20f22eeb9 100644 --- a/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py +++ b/src/openai/types/webhooks/eval_run_succeeded_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the eval run.""" class EvalRunSucceededWebhookEvent(BaseModel): + """Sent when an eval run has succeeded.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py b/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py index 1fe3c06096..0cfff85dad 100644 --- a/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py +++ b/src/openai/types/webhooks/fine_tuning_job_cancelled_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the fine-tuning job.""" class FineTuningJobCancelledWebhookEvent(BaseModel): + """Sent when a fine-tuning job has been cancelled.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py b/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py index 71d899c8ef..0eb6bf954f 100644 --- a/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py +++ b/src/openai/types/webhooks/fine_tuning_job_failed_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the fine-tuning job.""" class FineTuningJobFailedWebhookEvent(BaseModel): + """Sent when a fine-tuning job has failed.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py b/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py index 470f1fcfaa..26b5ea8955 100644 --- a/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py +++ b/src/openai/types/webhooks/fine_tuning_job_succeeded_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the fine-tuning job.""" class FineTuningJobSucceededWebhookEvent(BaseModel): + """Sent when a fine-tuning job has succeeded.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py index a166a3471b..4647a2e2ba 100644 --- a/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py +++ b/src/openai/types/webhooks/realtime_call_incoming_webhook_event.py @@ -9,6 +9,8 @@ class DataSipHeader(BaseModel): + """A header from the SIP Invite.""" + name: str """Name of the SIP Header.""" @@ -17,6 +19,8 @@ class DataSipHeader(BaseModel): class Data(BaseModel): + """Event data payload.""" + call_id: str """The unique ID of this call.""" @@ -25,6 +29,8 @@ class Data(BaseModel): class RealtimeCallIncomingWebhookEvent(BaseModel): + """Sent when Realtime API Receives a incoming SIP call.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/response_cancelled_webhook_event.py b/src/openai/types/webhooks/response_cancelled_webhook_event.py index 443e360e90..cd791b3314 100644 --- a/src/openai/types/webhooks/response_cancelled_webhook_event.py +++ b/src/openai/types/webhooks/response_cancelled_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the model response.""" class ResponseCancelledWebhookEvent(BaseModel): + """Sent when a background response has been cancelled.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/response_completed_webhook_event.py b/src/openai/types/webhooks/response_completed_webhook_event.py index ac1feff32b..cf07f0c2c0 100644 --- a/src/openai/types/webhooks/response_completed_webhook_event.py +++ b/src/openai/types/webhooks/response_completed_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the model response.""" class ResponseCompletedWebhookEvent(BaseModel): + """Sent when a background response has been completed.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/response_failed_webhook_event.py b/src/openai/types/webhooks/response_failed_webhook_event.py index 5b4ba65e18..aecb1b8f47 100644 --- a/src/openai/types/webhooks/response_failed_webhook_event.py +++ b/src/openai/types/webhooks/response_failed_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the model response.""" class ResponseFailedWebhookEvent(BaseModel): + """Sent when a background response has failed.""" + id: str """The unique ID of the event.""" diff --git a/src/openai/types/webhooks/response_incomplete_webhook_event.py b/src/openai/types/webhooks/response_incomplete_webhook_event.py index 01609314e0..2367731e85 100644 --- a/src/openai/types/webhooks/response_incomplete_webhook_event.py +++ b/src/openai/types/webhooks/response_incomplete_webhook_event.py @@ -9,11 +9,15 @@ class Data(BaseModel): + """Event data payload.""" + id: str """The unique ID of the model response.""" class ResponseIncompleteWebhookEvent(BaseModel): + """Sent when a background response has been interrupted.""" + id: str """The unique ID of the event.""" diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 14e2d911ef..d330eed134 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -361,22 +361,26 @@ def test_path_params_cancel(self, client: OpenAI) -> None: @parametrize def test_method_compact(self, client: OpenAI) -> None: - response = client.responses.compact() + response = client.responses.compact( + model="gpt-5.1", + ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize def test_method_compact_with_all_params(self, client: OpenAI) -> None: response = client.responses.compact( + model="gpt-5.1", input="string", instructions="instructions", - model="gpt-5.1", previous_response_id="resp_123", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize def test_raw_response_compact(self, client: OpenAI) -> None: - http_response = client.responses.with_raw_response.compact() + http_response = client.responses.with_raw_response.compact( + model="gpt-5.1", + ) assert http_response.is_closed is True assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -385,7 +389,9 @@ def test_raw_response_compact(self, client: OpenAI) -> None: @parametrize def test_streaming_response_compact(self, client: OpenAI) -> None: - with client.responses.with_streaming_response.compact() as http_response: + with client.responses.with_streaming_response.compact( + model="gpt-5.1", + ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -751,22 +757,26 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_compact(self, async_client: AsyncOpenAI) -> None: - response = await async_client.responses.compact() + response = await async_client.responses.compact( + model="gpt-5.1", + ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.compact( + model="gpt-5.1", input="string", instructions="instructions", - model="gpt-5.1", previous_response_id="resp_123", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: - http_response = await async_client.responses.with_raw_response.compact() + http_response = await async_client.responses.with_raw_response.compact( + model="gpt-5.1", + ) assert http_response.is_closed is True assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -775,7 +785,9 @@ async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None: - async with async_client.responses.with_streaming_response.compact() as http_response: + async with async_client.responses.with_streaming_response.compact( + model="gpt-5.1", + ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" From 752a293da54c72c248afee310fc7cc1324debc1d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:06:44 +0000 Subject: [PATCH 182/408] feat(api): gpt 5.2 --- .stats.yml | 4 ++-- src/openai/resources/beta/assistants.py | 8 ++++---- src/openai/resources/beta/threads/runs/runs.py | 12 ++++++------ .../resources/chat/completions/completions.py | 12 ++++++------ src/openai/resources/responses/responses.py | 10 ++++++++++ src/openai/types/beta/assistant_create_params.py | 2 +- src/openai/types/beta/assistant_update_params.py | 2 +- .../types/beta/threads/run_create_params.py | 2 +- .../types/chat/completion_create_params.py | 2 +- .../create_eval_completions_run_data_source.py | 2 +- ...ate_eval_completions_run_data_source_param.py | 2 +- src/openai/types/evals/run_cancel_response.py | 4 ++-- src/openai/types/evals/run_create_params.py | 4 ++-- src/openai/types/evals/run_create_response.py | 4 ++-- src/openai/types/evals/run_list_response.py | 4 ++-- src/openai/types/evals/run_retrieve_response.py | 4 ++-- src/openai/types/graders/score_model_grader.py | 8 ++++++-- .../types/graders/score_model_grader_param.py | 8 ++++++-- .../types/responses/response_compact_params.py | 5 +++++ src/openai/types/shared/chat_model.py | 5 +++++ src/openai/types/shared/reasoning.py | 5 +++-- src/openai/types/shared_params/chat_model.py | 5 +++++ src/openai/types/shared_params/reasoning.py | 5 +++-- tests/api_resources/test_responses.py | 16 ++++++++-------- 24 files changed, 85 insertions(+), 50 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0aa25fb4a4..4e5b38902d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-88d85ff87ad8983262af2b729762a6e05fd509468bb691529bc2f81e4ce27c69.yml -openapi_spec_hash: 46a55acbccd0147534017b92c1f4dd99 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-41f98da99f44ebe6204fce5c1dc9940f85f3472779e797b674c4fdc20306c77d.yml +openapi_spec_hash: c61259027f421f501bdc6b23cf9e430e config_hash: 141b101c9f13b90e21af74e1686f1f41 diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index aa1f9f9b48..ab0947abf4 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -108,7 +108,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -323,7 +323,7 @@ def update( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -577,7 +577,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -792,7 +792,7 @@ async def update( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 9b6cb3f752..8a58e91f69 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -179,7 +179,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -341,7 +341,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -499,7 +499,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1633,7 +1633,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1795,7 +1795,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1953,7 +1953,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 3f2732a608..9c0b74b804 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -421,7 +421,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -732,7 +732,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -1034,7 +1034,7 @@ def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -1907,7 +1907,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -2218,7 +2218,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. @@ -2520,7 +2520,7 @@ async def create( - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. response_format: An object specifying the format that the model must output. diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 81e8980faf..8e80f6793b 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1528,6 +1528,11 @@ def compact( *, model: Union[ Literal[ + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", "gpt-5.1", "gpt-5.1-2025-11-13", "gpt-5.1-codex", @@ -3141,6 +3146,11 @@ async def compact( *, model: Union[ Literal[ + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", "gpt-5.1", "gpt-5.1-2025-11-13", "gpt-5.1-codex", diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 49e7af2d67..461d871ab5 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -72,7 +72,7 @@ class AssistantCreateParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index d84b15cc5b..7896fcd9c6 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -107,7 +107,7 @@ class AssistantUpdateParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index f4c56feb56..376afc9aad 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -121,7 +121,7 @@ class RunCreateParamsBase(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 613787e9b5..49cefb95fc 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -207,7 +207,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ response_format: ResponseFormat diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 6ec39873b7..cc88450276 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -196,7 +196,7 @@ class SamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ response_format: Optional[SamplingParamsResponseFormat] = None diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 22d07c5598..bab2658afe 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -192,7 +192,7 @@ class SamplingParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ response_format: SamplingParamsResponseFormat diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 40f071c959..6ad874b2b3 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -115,7 +115,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -278,7 +278,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index 993e10c738..fd7d56e39d 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -128,7 +128,7 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ temperature: Optional[float] @@ -296,7 +296,7 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ seed: int diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 93dae7adde..1651cb5b2f 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -115,7 +115,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -278,7 +278,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index c3c745b21c..9ec84330cf 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -115,7 +115,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -278,7 +278,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index d02256c679..9204635d1e 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -115,7 +115,7 @@ class DataSourceResponsesSourceResponses(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ temperature: Optional[float] = None @@ -278,7 +278,7 @@ class DataSourceResponsesSamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index 6dd5a0eee8..bceb8b1ad0 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -91,7 +91,7 @@ class SamplingParams(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ seed: Optional[int] = None @@ -108,7 +108,11 @@ class ScoreModelGrader(BaseModel): """A ScoreModelGrader object that uses a model to assign a score to the input.""" input: List[Input] - """The input text. This may include template strings.""" + """The input messages evaluated by the grader. + + Supports text, output text, input image, and input audio content blocks, and may + include template strings. + """ model: str """The model to use for the evaluation.""" diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 33452e43f3..bd08645a85 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -97,7 +97,7 @@ class SamplingParams(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ seed: Optional[int] @@ -114,7 +114,11 @@ class ScoreModelGraderParam(TypedDict, total=False): """A ScoreModelGrader object that uses a model to assign a score to the input.""" input: Required[Iterable[Input]] - """The input text. This may include template strings.""" + """The input messages evaluated by the grader. + + Supports text, output text, input image, and input audio content blocks, and may + include template strings. + """ model: Required[str] """The model to use for the evaluation.""" diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index 35a390f807..657c6a0764 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -14,6 +14,11 @@ class ResponseCompactParams(TypedDict, total=False): model: Required[ Union[ Literal[ + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", "gpt-5.1", "gpt-5.1-2025-11-13", "gpt-5.1-codex", diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index b3ae7c3a95..8223b81bef 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -5,6 +5,11 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", "gpt-5.1", "gpt-5.1-2025-11-13", "gpt-5.1-codex", diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 1220a045b1..14f56a04cd 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -30,7 +30,7 @@ class Reasoning(BaseModel): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None @@ -47,5 +47,6 @@ class Reasoning(BaseModel): This can be useful for debugging and understanding the model's reasoning process. One of `auto`, `concise`, or `detailed`. - `concise` is only supported for `computer-use-preview` models. + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. """ diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index 7505d51d67..c1937a8312 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -7,6 +7,11 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", "gpt-5.1", "gpt-5.1-2025-11-13", "gpt-5.1-codex", diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 68d9c374b8..2bd7ce7268 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -31,7 +31,7 @@ class Reasoning(TypedDict, total=False): - All models before `gpt-5.1` default to `medium` reasoning effort, and do not support `none`. - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is currently only supported for `gpt-5.1-codex-max`. + - `xhigh` is supported for all models after `gpt-5.1-codex-max`. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] @@ -48,5 +48,6 @@ class Reasoning(TypedDict, total=False): This can be useful for debugging and understanding the model's reasoning process. One of `auto`, `concise`, or `detailed`. - `concise` is only supported for `computer-use-preview` models. + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. """ diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index d330eed134..3cdbdba8a6 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -362,14 +362,14 @@ def test_path_params_cancel(self, client: OpenAI) -> None: @parametrize def test_method_compact(self, client: OpenAI) -> None: response = client.responses.compact( - model="gpt-5.1", + model="gpt-5.2", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize def test_method_compact_with_all_params(self, client: OpenAI) -> None: response = client.responses.compact( - model="gpt-5.1", + model="gpt-5.2", input="string", instructions="instructions", previous_response_id="resp_123", @@ -379,7 +379,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_compact(self, client: OpenAI) -> None: http_response = client.responses.with_raw_response.compact( - model="gpt-5.1", + model="gpt-5.2", ) assert http_response.is_closed is True @@ -390,7 +390,7 @@ def test_raw_response_compact(self, client: OpenAI) -> None: @parametrize def test_streaming_response_compact(self, client: OpenAI) -> None: with client.responses.with_streaming_response.compact( - model="gpt-5.1", + model="gpt-5.2", ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -758,14 +758,14 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_compact(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.compact( - model="gpt-5.1", + model="gpt-5.2", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.compact( - model="gpt-5.1", + model="gpt-5.2", input="string", instructions="instructions", previous_response_id="resp_123", @@ -775,7 +775,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) - @parametrize async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: http_response = await async_client.responses.with_raw_response.compact( - model="gpt-5.1", + model="gpt-5.2", ) assert http_response.is_closed is True @@ -786,7 +786,7 @@ async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None: async with async_client.responses.with_streaming_response.compact( - model="gpt-5.1", + model="gpt-5.2", ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" From 43e324ed7a8a8b6f0738254c59d732be2807a06f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:07:20 +0000 Subject: [PATCH 183/408] release: 2.11.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 21f60560ae..e6c39adb99 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.10.0" + ".": "2.11.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 58a092665e..ff0878433f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.11.0 (2025-12-11) + +Full Changelog: [v2.10.0...v2.11.0](https://github.com/openai/openai-python/compare/v2.10.0...v2.11.0) + +### Features + +* **api:** gpt 5.2 ([dd9b8e8](https://github.com/openai/openai-python/commit/dd9b8e85cf91fe0d7470143fba10fe950ec740c4)) + ## 2.10.0 (2025-12-10) Full Changelog: [v2.9.0...v2.10.0](https://github.com/openai/openai-python/compare/v2.9.0...v2.10.0) diff --git a/pyproject.toml b/pyproject.toml index e7d181d007..f4e5bdb208 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.10.0" +version = "2.11.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index c7a4f08f04..16036c3b6a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.10.0" # x-release-please-version +__version__ = "2.11.0" # x-release-please-version From caa4a48feae6848d007fe5de39a747759f6baec6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:06:47 +0000 Subject: [PATCH 184/408] feat(api): api update --- .stats.yml | 4 +- src/openai/types/eval_create_params.py | 57 +++++++++++++++-- ...create_eval_completions_run_data_source.py | 57 +++++++++++++++-- ..._eval_completions_run_data_source_param.py | 58 +++++++++++++++-- src/openai/types/evals/run_cancel_response.py | 59 ++++++++++++++++-- src/openai/types/evals/run_create_params.py | 59 ++++++++++++++++-- src/openai/types/evals/run_create_response.py | 59 ++++++++++++++++-- src/openai/types/evals/run_list_response.py | 59 ++++++++++++++++-- .../types/evals/run_retrieve_response.py | 59 ++++++++++++++++-- .../types/graders/label_model_grader.py | 62 +++++++++++++++++-- .../types/graders/label_model_grader_param.py | 59 ++++++++++++++++-- .../types/graders/score_model_grader.py | 56 +++++++++++++++-- .../types/graders/score_model_grader_param.py | 54 ++++++++++++++-- src/openai/types/video_model.py | 4 +- 14 files changed, 633 insertions(+), 73 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4e5b38902d..595620b8ac 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-41f98da99f44ebe6204fce5c1dc9940f85f3472779e797b674c4fdc20306c77d.yml -openapi_spec_hash: c61259027f421f501bdc6b23cf9e430e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-386042697a8769999956bdc26ff1e70bfc2a56913f724eedc6bfaf82679e9956.yml +openapi_spec_hash: 7072a6a4a43d7ff0bb4098a3e8a6b9a7 config_hash: 141b101c9f13b90e21af74e1686f1f41 diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index 0f2100b718..bfb39568c2 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -27,7 +27,10 @@ "TestingCriterionLabelModelInputEvalItem", "TestingCriterionLabelModelInputEvalItemContent", "TestingCriterionLabelModelInputEvalItemContentOutputText", - "TestingCriterionLabelModelInputEvalItemContentInputImage", + "TestingCriterionLabelModelInputEvalItemContentEvalItemInputImage", + "TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "TestingCriterionTextSimilarity", "TestingCriterionPython", "TestingCriterionScoreModel", @@ -128,8 +131,8 @@ class TestingCriterionLabelModelInputEvalItemContentOutputText(TypedDict, total= """The type of the output text. Always `output_text`.""" -class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total=False): - """An image input to the model.""" +class TestingCriterionLabelModelInputEvalItemContentEvalItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" image_url: Required[str] """The URL of the image input.""" @@ -144,13 +147,51 @@ class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total= """ +class TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + TypedDict, total=False +): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + TypedDict, total=False +): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputTextParam, + TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudioParam, +] + TestingCriterionLabelModelInputEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, TestingCriterionLabelModelInputEvalItemContentOutputText, - TestingCriterionLabelModelInputEvalItemContentInputImage, + TestingCriterionLabelModelInputEvalItemContentEvalItemInputImage, ResponseInputAudioParam, - Iterable[object], + SequenceNotStr[TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], ] @@ -164,7 +205,11 @@ class TestingCriterionLabelModelInputEvalItem(TypedDict, total=False): """ content: Required[TestingCriterionLabelModelInputEvalItemContent] - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index cc88450276..34118c0dfb 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -28,7 +28,10 @@ "InputMessagesTemplateTemplateEvalItem", "InputMessagesTemplateTemplateEvalItemContent", "InputMessagesTemplateTemplateEvalItemContentOutputText", - "InputMessagesTemplateTemplateEvalItemContentInputImage", + "InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", + "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -101,8 +104,8 @@ class InputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): - """An image input to the model.""" +class InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" image_url: str """The URL of the image input.""" @@ -117,13 +120,51 @@ class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): """ +class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + BaseModel +): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + BaseModel +): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputText, + InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudio, +] + InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, InputMessagesTemplateTemplateEvalItemContentOutputText, - InputMessagesTemplateTemplateEvalItemContentInputImage, + InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, ResponseInputAudio, - List[object], + List[InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], ] @@ -137,7 +178,11 @@ class InputMessagesTemplateTemplateEvalItem(BaseModel): """ content: InputMessagesTemplateTemplateEvalItemContent - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index bab2658afe..64c4844d43 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -5,6 +5,7 @@ from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort from ..responses.easy_input_message_param import EasyInputMessageParam @@ -28,7 +29,10 @@ "InputMessagesTemplateTemplateEvalItem", "InputMessagesTemplateTemplateEvalItemContent", "InputMessagesTemplateTemplateEvalItemContentOutputText", - "InputMessagesTemplateTemplateEvalItemContentInputImage", + "InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", + "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -99,8 +103,8 @@ class InputMessagesTemplateTemplateEvalItemContentOutputText(TypedDict, total=Fa """The type of the output text. Always `output_text`.""" -class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=False): - """An image input to the model.""" +class InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" image_url: Required[str] """The URL of the image input.""" @@ -115,13 +119,51 @@ class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=Fa """ +class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + TypedDict, total=False +): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + TypedDict, total=False +): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputTextParam, + InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudioParam, +] + InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, InputMessagesTemplateTemplateEvalItemContentOutputText, - InputMessagesTemplateTemplateEvalItemContentInputImage, + InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, ResponseInputAudioParam, - Iterable[object], + SequenceNotStr[InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], ] @@ -135,7 +177,11 @@ class InputMessagesTemplateTemplateEvalItem(TypedDict, total=False): """ content: Required[InputMessagesTemplateTemplateEvalItemContent] - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 6ad874b2b3..791b1f0444 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -33,7 +33,10 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -155,8 +158,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): - """An image input to the model.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" image_url: str """The URL of the image input.""" @@ -171,13 +174,53 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( """ +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + BaseModel +): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + BaseModel +): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudio, +] + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, ResponseInputAudio, - List[object], + List[ + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio + ], ] @@ -191,7 +234,11 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): """ content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index fd7d56e39d..0717f2ee20 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -31,7 +31,10 @@ "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference", "DataSourceCreateEvalResponsesRunDataSourceSamplingParams", "DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText", @@ -171,10 +174,10 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva """The type of the output text. Always `output_text`.""" -class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage( +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage( TypedDict, total=False ): - """An image input to the model.""" + """An image input block used within EvalItem content arrays.""" image_url: Required[str] """The URL of the image input.""" @@ -189,13 +192,53 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva """ +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + TypedDict, total=False +): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + TypedDict, total=False +): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputTextParam, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudioParam, +] + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, ResponseInputAudioParam, - Iterable[object], + SequenceNotStr[ + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio + ], ] @@ -209,7 +252,11 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva """ content: Required[DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent] - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 1651cb5b2f..a860bd178a 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -33,7 +33,10 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -155,8 +158,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): - """An image input to the model.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" image_url: str """The URL of the image input.""" @@ -171,13 +174,53 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( """ +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + BaseModel +): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + BaseModel +): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudio, +] + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, ResponseInputAudio, - List[object], + List[ + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio + ], ] @@ -191,7 +234,11 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): """ content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index 9ec84330cf..aee471e2b6 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -33,7 +33,10 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -155,8 +158,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): - """An image input to the model.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" image_url: str """The URL of the image input.""" @@ -171,13 +174,53 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( """ +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + BaseModel +): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + BaseModel +): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudio, +] + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, ResponseInputAudio, - List[object], + List[ + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio + ], ] @@ -191,7 +234,11 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): """ content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index 9204635d1e..ff3564de54 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -33,7 +33,10 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -155,8 +158,8 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): - """An image input to the model.""" +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" image_url: str """The URL of the image input.""" @@ -171,13 +174,53 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage( """ +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( + BaseModel +): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( + BaseModel +): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudio, +] + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, ResponseInputAudio, - List[object], + List[ + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio + ], ] @@ -191,7 +234,11 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItem(BaseModel): """ content: DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/graders/label_model_grader.py b/src/openai/types/graders/label_model_grader.py index 141306b510..def3dd7602 100644 --- a/src/openai/types/graders/label_model_grader.py +++ b/src/openai/types/graders/label_model_grader.py @@ -7,7 +7,16 @@ from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio -__all__ = ["LabelModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] +__all__ = [ + "LabelModelGrader", + "Input", + "InputContent", + "InputContentOutputText", + "InputContentEvalItemInputImage", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", +] class InputContentOutputText(BaseModel): @@ -20,8 +29,34 @@ class InputContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -class InputContentInputImage(BaseModel): - """An image input to the model.""" +class InputContentEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" image_url: str """The URL of the image input.""" @@ -36,8 +71,21 @@ class InputContentInputImage(BaseModel): """ +InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputText, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudio, +] + InputContent: TypeAlias = Union[ - str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, List[object] + str, + ResponseInputText, + InputContentOutputText, + InputContentEvalItemInputImage, + ResponseInputAudio, + List[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], ] @@ -51,7 +99,11 @@ class Input(BaseModel): """ content: InputContent - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py index a23be2a236..6b6faa465d 100644 --- a/src/openai/types/graders/label_model_grader_param.py +++ b/src/openai/types/graders/label_model_grader_param.py @@ -9,7 +9,16 @@ from ..responses.response_input_text_param import ResponseInputTextParam from ..responses.response_input_audio_param import ResponseInputAudioParam -__all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] +__all__ = [ + "LabelModelGraderParam", + "Input", + "InputContent", + "InputContentOutputText", + "InputContentEvalItemInputImage", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", +] class InputContentOutputText(TypedDict, total=False): @@ -22,8 +31,8 @@ class InputContentOutputText(TypedDict, total=False): """The type of the output text. Always `output_text`.""" -class InputContentInputImage(TypedDict, total=False): - """An image input to the model.""" +class InputContentEvalItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" image_url: Required[str] """The URL of the image input.""" @@ -38,13 +47,47 @@ class InputContentInputImage(TypedDict, total=False): """ +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputTextParam, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudioParam, +] + InputContent: TypeAlias = Union[ str, ResponseInputTextParam, InputContentOutputText, - InputContentInputImage, + InputContentEvalItemInputImage, ResponseInputAudioParam, - Iterable[object], + SequenceNotStr[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], ] @@ -58,7 +101,11 @@ class Input(TypedDict, total=False): """ content: Required[InputContent] - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index bceb8b1ad0..7787c82bb6 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -13,7 +13,10 @@ "Input", "InputContent", "InputContentOutputText", - "InputContentInputImage", + "InputContentEvalItemInputImage", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "SamplingParams", ] @@ -28,8 +31,8 @@ class InputContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -class InputContentInputImage(BaseModel): - """An image input to the model.""" +class InputContentEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" image_url: str """The URL of the image input.""" @@ -44,8 +47,47 @@ class InputContentInputImage(BaseModel): """ +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputText, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudio, +] + InputContent: TypeAlias = Union[ - str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, List[object] + str, + ResponseInputText, + InputContentOutputText, + InputContentEvalItemInputImage, + ResponseInputAudio, + List[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], ] @@ -59,7 +101,11 @@ class Input(BaseModel): """ content: InputContent - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Literal["user", "assistant", "system", "developer"] """The role of the message input. diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index bd08645a85..1f9475d885 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -5,6 +5,7 @@ from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text_param import ResponseInputTextParam from ..responses.response_input_audio_param import ResponseInputAudioParam @@ -14,7 +15,10 @@ "Input", "InputContent", "InputContentOutputText", - "InputContentInputImage", + "InputContentEvalItemInputImage", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", + "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", "SamplingParams", ] @@ -29,8 +33,8 @@ class InputContentOutputText(TypedDict, total=False): """The type of the output text. Always `output_text`.""" -class InputContentInputImage(TypedDict, total=False): - """An image input to the model.""" +class InputContentEvalItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" image_url: Required[str] """The URL of the image input.""" @@ -45,13 +49,47 @@ class InputContentInputImage(TypedDict, total=False): """ +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ + str, + ResponseInputTextParam, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, + InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, + ResponseInputAudioParam, +] + InputContent: TypeAlias = Union[ str, ResponseInputTextParam, InputContentOutputText, - InputContentInputImage, + InputContentEvalItemInputImage, ResponseInputAudioParam, - Iterable[object], + SequenceNotStr[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], ] @@ -65,7 +103,11 @@ class Input(TypedDict, total=False): """ content: Required[InputContent] - """Inputs to the model - can contain template strings.""" + """Inputs to the model - can contain template strings. + + Supports text, output text, input images, and input audio, either as a single + item or an array of items. + """ role: Required[Literal["user", "assistant", "system", "developer"]] """The role of the message input. diff --git a/src/openai/types/video_model.py b/src/openai/types/video_model.py index 0b0835fca4..e96e7685f8 100644 --- a/src/openai/types/video_model.py +++ b/src/openai/types/video_model.py @@ -4,4 +4,6 @@ __all__ = ["VideoModel"] -VideoModel: TypeAlias = Literal["sora-2", "sora-2-pro"] +VideoModel: TypeAlias = Literal[ + "sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-pro-2025-10-06", "sora-2-2025-12-08" +] From 8bb16f7aedf919646e66ddc591fe283d770b8257 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 13 Dec 2025 03:23:48 +0000 Subject: [PATCH 185/408] feat(api): fix grader input list, add dated slugs for sora-2 --- .stats.yml | 6 +- api.md | 1 + src/openai/types/eval_create_params.py | 50 ++--------------- ...create_eval_completions_run_data_source.py | 50 ++--------------- ..._eval_completions_run_data_source_param.py | 51 ++--------------- src/openai/types/evals/run_cancel_response.py | 52 ++---------------- src/openai/types/evals/run_create_params.py | 52 ++---------------- src/openai/types/evals/run_create_response.py | 52 ++---------------- src/openai/types/evals/run_list_response.py | 52 ++---------------- .../types/evals/run_retrieve_response.py | 52 ++---------------- src/openai/types/graders/__init__.py | 2 + src/openai/types/graders/grader_inputs.py | 43 +++++++++++++++ .../types/graders/grader_inputs_param.py | 53 ++++++++++++++++++ .../types/graders/label_model_grader.py | 55 ++----------------- .../types/graders/label_model_grader_param.py | 52 ++---------------- .../types/graders/score_model_grader.py | 49 ++--------------- .../types/graders/score_model_grader_param.py | 47 ++-------------- 17 files changed, 160 insertions(+), 559 deletions(-) create mode 100644 src/openai/types/graders/grader_inputs.py create mode 100644 src/openai/types/graders/grader_inputs_param.py diff --git a/.stats.yml b/.stats.yml index 595620b8ac..2d7e7248c7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-386042697a8769999956bdc26ff1e70bfc2a56913f724eedc6bfaf82679e9956.yml -openapi_spec_hash: 7072a6a4a43d7ff0bb4098a3e8a6b9a7 -config_hash: 141b101c9f13b90e21af74e1686f1f41 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fe8e67bdc351a518b113ab48e775750190e207807903d6b03ab22c438c38a588.yml +openapi_spec_hash: 8af972190647ffb9dcec516e19d8761a +config_hash: 856bee50ee3617e85a9bc9274db01dbb diff --git a/api.md b/api.md index 3807603206..1c054b52fc 100644 --- a/api.md +++ b/api.md @@ -342,6 +342,7 @@ Types: ```python from openai.types.graders import ( + GraderInputs, LabelModelGrader, MultiGrader, PythonGrader, diff --git a/src/openai/types/eval_create_params.py b/src/openai/types/eval_create_params.py index bfb39568c2..a1d5ea5371 100644 --- a/src/openai/types/eval_create_params.py +++ b/src/openai/types/eval_create_params.py @@ -7,6 +7,7 @@ from .._types import SequenceNotStr from .shared_params.metadata import Metadata +from .graders.grader_inputs_param import GraderInputsParam from .graders.python_grader_param import PythonGraderParam from .graders.score_model_grader_param import ScoreModelGraderParam from .graders.string_check_grader_param import StringCheckGraderParam @@ -27,10 +28,7 @@ "TestingCriterionLabelModelInputEvalItem", "TestingCriterionLabelModelInputEvalItemContent", "TestingCriterionLabelModelInputEvalItemContentOutputText", - "TestingCriterionLabelModelInputEvalItemContentEvalItemInputImage", - "TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "TestingCriterionLabelModelInputEvalItemContentInputImage", "TestingCriterionTextSimilarity", "TestingCriterionPython", "TestingCriterionScoreModel", @@ -131,7 +129,7 @@ class TestingCriterionLabelModelInputEvalItemContentOutputText(TypedDict, total= """The type of the output text. Always `output_text`.""" -class TestingCriterionLabelModelInputEvalItemContentEvalItemInputImage(TypedDict, total=False): +class TestingCriterionLabelModelInputEvalItemContentInputImage(TypedDict, total=False): """An image input block used within EvalItem content arrays.""" image_url: Required[str] @@ -147,51 +145,13 @@ class TestingCriterionLabelModelInputEvalItemContentEvalItemInputImage(TypedDict """ -class TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - TypedDict, total=False -): - """A text output from the model.""" - - text: Required[str] - """The text output from the model.""" - - type: Required[Literal["output_text"]] - """The type of the output text. Always `output_text`.""" - - -class TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - TypedDict, total=False -): - """An image input block used within EvalItem content arrays.""" - - image_url: Required[str] - """The URL of the image input.""" - - type: Required[Literal["input_image"]] - """The type of the image input. Always `input_image`.""" - - detail: str - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputTextParam, - TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudioParam, -] - TestingCriterionLabelModelInputEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, TestingCriterionLabelModelInputEvalItemContentOutputText, - TestingCriterionLabelModelInputEvalItemContentEvalItemInputImage, + TestingCriterionLabelModelInputEvalItemContentInputImage, ResponseInputAudioParam, - SequenceNotStr[TestingCriterionLabelModelInputEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], + GraderInputsParam, ] diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 34118c0dfb..726ae6abf0 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -6,6 +6,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs from ..shared.reasoning_effort import ReasoningEffort from ..shared.response_format_text import ResponseFormatText from ..responses.easy_input_message import EasyInputMessage @@ -28,10 +29,7 @@ "InputMessagesTemplateTemplateEvalItem", "InputMessagesTemplateTemplateEvalItemContent", "InputMessagesTemplateTemplateEvalItemContentOutputText", - "InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", - "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "InputMessagesTemplateTemplateEvalItemContentInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -104,7 +102,7 @@ class InputMessagesTemplateTemplateEvalItemContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -class InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): +class InputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): """An image input block used within EvalItem content arrays.""" image_url: str @@ -120,51 +118,13 @@ class InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): """ -class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - BaseModel -): - """A text output from the model.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - BaseModel -): - """An image input block used within EvalItem content arrays.""" - - image_url: str - """The URL of the image input.""" - - type: Literal["input_image"] - """The type of the image input. Always `input_image`.""" - - detail: Optional[str] = None - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputText, - InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudio, -] - InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, InputMessagesTemplateTemplateEvalItemContentOutputText, - InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, + InputMessagesTemplateTemplateEvalItemContentInputImage, ResponseInputAudio, - List[InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], + GraderInputs, ] diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 64c4844d43..6842f84af9 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -5,9 +5,9 @@ from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ..._types import SequenceNotStr from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort +from ..graders.grader_inputs_param import GraderInputsParam from ..responses.easy_input_message_param import EasyInputMessageParam from ..shared_params.response_format_text import ResponseFormatText from ..responses.response_input_text_param import ResponseInputTextParam @@ -29,10 +29,7 @@ "InputMessagesTemplateTemplateEvalItem", "InputMessagesTemplateTemplateEvalItemContent", "InputMessagesTemplateTemplateEvalItemContentOutputText", - "InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", - "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "InputMessagesTemplateTemplateEvalItemContentInputImage", "InputMessagesItemReference", "SamplingParams", "SamplingParamsResponseFormat", @@ -103,7 +100,7 @@ class InputMessagesTemplateTemplateEvalItemContentOutputText(TypedDict, total=Fa """The type of the output text. Always `output_text`.""" -class InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(TypedDict, total=False): +class InputMessagesTemplateTemplateEvalItemContentInputImage(TypedDict, total=False): """An image input block used within EvalItem content arrays.""" image_url: Required[str] @@ -119,51 +116,13 @@ class InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(TypedDict, """ -class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - TypedDict, total=False -): - """A text output from the model.""" - - text: Required[str] - """The text output from the model.""" - - type: Required[Literal["output_text"]] - """The type of the output text. Always `output_text`.""" - - -class InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - TypedDict, total=False -): - """An image input block used within EvalItem content arrays.""" - - image_url: Required[str] - """The URL of the image input.""" - - type: Required[Literal["input_image"]] - """The type of the image input. Always `input_image`.""" - - detail: str - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputTextParam, - InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudioParam, -] - InputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, InputMessagesTemplateTemplateEvalItemContentOutputText, - InputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, + InputMessagesTemplateTemplateEvalItemContentInputImage, ResponseInputAudioParam, - SequenceNotStr[InputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], + GraderInputsParam, ] diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index 791b1f0444..ea4797eecb 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -10,6 +10,7 @@ from .eval_api_error import EvalAPIError from ..responses.tool import Tool from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio @@ -33,10 +34,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -158,7 +156,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): """An image input block used within EvalItem content arrays.""" image_url: str @@ -174,53 +172,13 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInp """ -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - BaseModel -): - """A text output from the model.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - BaseModel -): - """An image input block used within EvalItem content arrays.""" - - image_url: str - """The URL of the image input.""" - - type: Literal["input_image"] - """The type of the image input. Always `input_image`.""" - - detail: Optional[str] = None - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudio, -] - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, ResponseInputAudio, - List[ - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio - ], + GraderInputs, ] diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index 0717f2ee20..02804c30da 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -9,6 +9,7 @@ from ..responses.tool_param import ToolParam from ..shared_params.metadata import Metadata from ..shared.reasoning_effort import ReasoningEffort +from ..graders.grader_inputs_param import GraderInputsParam from ..responses.response_input_text_param import ResponseInputTextParam from ..responses.response_input_audio_param import ResponseInputAudioParam from .create_eval_jsonl_run_data_source_param import CreateEvalJSONLRunDataSourceParam @@ -31,10 +32,7 @@ "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItem", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", - "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceCreateEvalResponsesRunDataSourceInputMessagesItemReference", "DataSourceCreateEvalResponsesRunDataSourceSamplingParams", "DataSourceCreateEvalResponsesRunDataSourceSamplingParamsText", @@ -174,7 +172,7 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva """The type of the output text. Always `output_text`.""" -class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage( +class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage( TypedDict, total=False ): """An image input block used within EvalItem content arrays.""" @@ -192,53 +190,13 @@ class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEva """ -class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - TypedDict, total=False -): - """A text output from the model.""" - - text: Required[str] - """The text output from the model.""" - - type: Required[Literal["output_text"]] - """The type of the output text. Always `output_text`.""" - - -class DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - TypedDict, total=False -): - """An image input block used within EvalItem content arrays.""" - - image_url: Required[str] - """The URL of the image input.""" - - type: Required[Literal["input_image"]] - """The type of the image input. Always `input_image`.""" - - detail: str - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputTextParam, - DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudioParam, -] - DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputTextParam, DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, + DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentInputImage, ResponseInputAudioParam, - SequenceNotStr[ - DataSourceCreateEvalResponsesRunDataSourceInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio - ], + GraderInputsParam, ] diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index a860bd178a..2cb856de6f 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -10,6 +10,7 @@ from .eval_api_error import EvalAPIError from ..responses.tool import Tool from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio @@ -33,10 +34,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -158,7 +156,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): """An image input block used within EvalItem content arrays.""" image_url: str @@ -174,53 +172,13 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInp """ -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - BaseModel -): - """A text output from the model.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - BaseModel -): - """An image input block used within EvalItem content arrays.""" - - image_url: str - """The URL of the image input.""" - - type: Literal["input_image"] - """The type of the image input. Always `input_image`.""" - - detail: Optional[str] = None - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudio, -] - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, ResponseInputAudio, - List[ - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio - ], + GraderInputs, ] diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index aee471e2b6..defd4aa6f9 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -10,6 +10,7 @@ from .eval_api_error import EvalAPIError from ..responses.tool import Tool from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio @@ -33,10 +34,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -158,7 +156,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): """An image input block used within EvalItem content arrays.""" image_url: str @@ -174,53 +172,13 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInp """ -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - BaseModel -): - """A text output from the model.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - BaseModel -): - """An image input block used within EvalItem content arrays.""" - - image_url: str - """The URL of the image input.""" - - type: Literal["input_image"] - """The type of the image input. Always `input_image`.""" - - detail: Optional[str] = None - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudio, -] - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, ResponseInputAudio, - List[ - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio - ], + GraderInputs, ] diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index ff3564de54..4c218a0510 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -10,6 +10,7 @@ from .eval_api_error import EvalAPIError from ..responses.tool import Tool from ..shared.metadata import Metadata +from ..graders.grader_inputs import GraderInputs from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio @@ -33,10 +34,7 @@ "DataSourceResponsesInputMessagesTemplateTemplateEvalItem", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent", "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage", "DataSourceResponsesInputMessagesItemReference", "DataSourceResponsesSamplingParams", "DataSourceResponsesSamplingParamsText", @@ -158,7 +156,7 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText( """The type of the output text. Always `output_text`.""" -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage(BaseModel): +class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage(BaseModel): """An image input block used within EvalItem content arrays.""" image_url: str @@ -174,53 +172,13 @@ class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInp """ -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText( - BaseModel -): - """A text output from the model.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -class DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage( - BaseModel -): - """An image input block used within EvalItem content arrays.""" - - image_url: str - """The URL of the image input.""" - - type: Literal["input_image"] - """The type of the image input. Always `input_image`.""" - - detail: Optional[str] = None - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudio, -] - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContent: TypeAlias = Union[ str, ResponseInputText, DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentOutputText, - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentEvalItemInputImage, + DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentInputImage, ResponseInputAudio, - List[ - DataSourceResponsesInputMessagesTemplateTemplateEvalItemContentAnArrayOfInputTextOutputTextInputImageAndInputAudio - ], + GraderInputs, ] diff --git a/src/openai/types/graders/__init__.py b/src/openai/types/graders/__init__.py index e0a909125e..4f70eb6c2f 100644 --- a/src/openai/types/graders/__init__.py +++ b/src/openai/types/graders/__init__.py @@ -3,10 +3,12 @@ from __future__ import annotations from .multi_grader import MultiGrader as MultiGrader +from .grader_inputs import GraderInputs as GraderInputs from .python_grader import PythonGrader as PythonGrader from .label_model_grader import LabelModelGrader as LabelModelGrader from .multi_grader_param import MultiGraderParam as MultiGraderParam from .score_model_grader import ScoreModelGrader as ScoreModelGrader +from .grader_inputs_param import GraderInputsParam as GraderInputsParam from .python_grader_param import PythonGraderParam as PythonGraderParam from .string_check_grader import StringCheckGrader as StringCheckGrader from .text_similarity_grader import TextSimilarityGrader as TextSimilarityGrader diff --git a/src/openai/types/graders/grader_inputs.py b/src/openai/types/graders/grader_inputs.py new file mode 100644 index 0000000000..edc966d889 --- /dev/null +++ b/src/openai/types/graders/grader_inputs.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from ..responses.response_input_text import ResponseInputText +from ..responses.response_input_audio import ResponseInputAudio + +__all__ = ["GraderInputs", "GraderInputItem", "GraderInputItemOutputText", "GraderInputItemInputImage"] + + +class GraderInputItemOutputText(BaseModel): + """A text output from the model.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + +class GraderInputItemInputImage(BaseModel): + """An image input block used within EvalItem content arrays.""" + + image_url: str + """The URL of the image input.""" + + type: Literal["input_image"] + """The type of the image input. Always `input_image`.""" + + detail: Optional[str] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +GraderInputItem: TypeAlias = Union[ + str, ResponseInputText, GraderInputItemOutputText, GraderInputItemInputImage, ResponseInputAudio +] + +GraderInputs: TypeAlias = List[GraderInputItem] diff --git a/src/openai/types/graders/grader_inputs_param.py b/src/openai/types/graders/grader_inputs_param.py new file mode 100644 index 0000000000..7d8341eb32 --- /dev/null +++ b/src/openai/types/graders/grader_inputs_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..responses.response_input_text_param import ResponseInputTextParam +from ..responses.response_input_audio_param import ResponseInputAudioParam + +__all__ = [ + "GraderInputsParam", + "GraderInputsParamItem", + "GraderInputsParamItemOutputText", + "GraderInputsParamItemInputImage", +] + + +class GraderInputsParamItemOutputText(TypedDict, total=False): + """A text output from the model.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + +class GraderInputsParamItemInputImage(TypedDict, total=False): + """An image input block used within EvalItem content arrays.""" + + image_url: Required[str] + """The URL of the image input.""" + + type: Required[Literal["input_image"]] + """The type of the image input. Always `input_image`.""" + + detail: str + """The detail level of the image to be sent to the model. + + One of `high`, `low`, or `auto`. Defaults to `auto`. + """ + + +GraderInputsParamItem: TypeAlias = Union[ + str, + ResponseInputTextParam, + GraderInputsParamItemOutputText, + GraderInputsParamItemInputImage, + ResponseInputAudioParam, +] + +GraderInputsParam: TypeAlias = List[GraderInputsParamItem] diff --git a/src/openai/types/graders/label_model_grader.py b/src/openai/types/graders/label_model_grader.py index def3dd7602..d3c942235e 100644 --- a/src/openai/types/graders/label_model_grader.py +++ b/src/openai/types/graders/label_model_grader.py @@ -4,19 +4,11 @@ from typing_extensions import Literal, TypeAlias from ..._models import BaseModel +from .grader_inputs import GraderInputs from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio -__all__ = [ - "LabelModelGrader", - "Input", - "InputContent", - "InputContentOutputText", - "InputContentEvalItemInputImage", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", -] +__all__ = ["LabelModelGrader", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] class InputContentOutputText(BaseModel): @@ -29,7 +21,7 @@ class InputContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -class InputContentEvalItemInputImage(BaseModel): +class InputContentInputImage(BaseModel): """An image input block used within EvalItem content arrays.""" image_url: str @@ -45,47 +37,8 @@ class InputContentEvalItemInputImage(BaseModel): """ -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(BaseModel): - """A text output from the model.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(BaseModel): - """An image input block used within EvalItem content arrays.""" - - image_url: str - """The URL of the image input.""" - - type: Literal["input_image"] - """The type of the image input. Always `input_image`.""" - - detail: Optional[str] = None - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputText, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudio, -] - InputContent: TypeAlias = Union[ - str, - ResponseInputText, - InputContentOutputText, - InputContentEvalItemInputImage, - ResponseInputAudio, - List[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], + str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, GraderInputs ] diff --git a/src/openai/types/graders/label_model_grader_param.py b/src/openai/types/graders/label_model_grader_param.py index 6b6faa465d..a5b6959cff 100644 --- a/src/openai/types/graders/label_model_grader_param.py +++ b/src/openai/types/graders/label_model_grader_param.py @@ -6,19 +6,11 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .grader_inputs_param import GraderInputsParam from ..responses.response_input_text_param import ResponseInputTextParam from ..responses.response_input_audio_param import ResponseInputAudioParam -__all__ = [ - "LabelModelGraderParam", - "Input", - "InputContent", - "InputContentOutputText", - "InputContentEvalItemInputImage", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", -] +__all__ = ["LabelModelGraderParam", "Input", "InputContent", "InputContentOutputText", "InputContentInputImage"] class InputContentOutputText(TypedDict, total=False): @@ -31,33 +23,7 @@ class InputContentOutputText(TypedDict, total=False): """The type of the output text. Always `output_text`.""" -class InputContentEvalItemInputImage(TypedDict, total=False): - """An image input block used within EvalItem content arrays.""" - - image_url: Required[str] - """The URL of the image input.""" - - type: Required[Literal["input_image"]] - """The type of the image input. Always `input_image`.""" - - detail: str - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(TypedDict, total=False): - """A text output from the model.""" - - text: Required[str] - """The text output from the model.""" - - type: Required[Literal["output_text"]] - """The type of the output text. Always `output_text`.""" - - -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(TypedDict, total=False): +class InputContentInputImage(TypedDict, total=False): """An image input block used within EvalItem content arrays.""" image_url: Required[str] @@ -73,21 +39,13 @@ class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInp """ -InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputTextParam, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudioParam, -] - InputContent: TypeAlias = Union[ str, ResponseInputTextParam, InputContentOutputText, - InputContentEvalItemInputImage, + InputContentInputImage, ResponseInputAudioParam, - SequenceNotStr[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], + GraderInputsParam, ] diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index 7787c82bb6..85d11e8666 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -4,6 +4,7 @@ from typing_extensions import Literal, TypeAlias from ..._models import BaseModel +from .grader_inputs import GraderInputs from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text import ResponseInputText from ..responses.response_input_audio import ResponseInputAudio @@ -13,10 +14,7 @@ "Input", "InputContent", "InputContentOutputText", - "InputContentEvalItemInputImage", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "InputContentInputImage", "SamplingParams", ] @@ -31,7 +29,7 @@ class InputContentOutputText(BaseModel): """The type of the output text. Always `output_text`.""" -class InputContentEvalItemInputImage(BaseModel): +class InputContentInputImage(BaseModel): """An image input block used within EvalItem content arrays.""" image_url: str @@ -47,47 +45,8 @@ class InputContentEvalItemInputImage(BaseModel): """ -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(BaseModel): - """A text output from the model.""" - - text: str - """The text output from the model.""" - - type: Literal["output_text"] - """The type of the output text. Always `output_text`.""" - - -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(BaseModel): - """An image input block used within EvalItem content arrays.""" - - image_url: str - """The URL of the image input.""" - - type: Literal["input_image"] - """The type of the image input. Always `input_image`.""" - - detail: Optional[str] = None - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputText, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudio, -] - InputContent: TypeAlias = Union[ - str, - ResponseInputText, - InputContentOutputText, - InputContentEvalItemInputImage, - ResponseInputAudio, - List[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], + str, ResponseInputText, InputContentOutputText, InputContentInputImage, ResponseInputAudio, GraderInputs ] diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 1f9475d885..9f1c42e051 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -5,7 +5,7 @@ from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict -from ..._types import SequenceNotStr +from .grader_inputs_param import GraderInputsParam from ..shared.reasoning_effort import ReasoningEffort from ..responses.response_input_text_param import ResponseInputTextParam from ..responses.response_input_audio_param import ResponseInputAudioParam @@ -15,10 +15,7 @@ "Input", "InputContent", "InputContentOutputText", - "InputContentEvalItemInputImage", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText", - "InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage", + "InputContentInputImage", "SamplingParams", ] @@ -33,7 +30,7 @@ class InputContentOutputText(TypedDict, total=False): """The type of the output text. Always `output_text`.""" -class InputContentEvalItemInputImage(TypedDict, total=False): +class InputContentInputImage(TypedDict, total=False): """An image input block used within EvalItem content arrays.""" image_url: Required[str] @@ -49,47 +46,13 @@ class InputContentEvalItemInputImage(TypedDict, total=False): """ -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText(TypedDict, total=False): - """A text output from the model.""" - - text: Required[str] - """The text output from the model.""" - - type: Required[Literal["output_text"]] - """The type of the output text. Always `output_text`.""" - - -class InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage(TypedDict, total=False): - """An image input block used within EvalItem content arrays.""" - - image_url: Required[str] - """The URL of the image input.""" - - type: Required[Literal["input_image"]] - """The type of the image input. Always `input_image`.""" - - detail: str - """The detail level of the image to be sent to the model. - - One of `high`, `low`, or `auto`. Defaults to `auto`. - """ - - -InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio: TypeAlias = Union[ - str, - ResponseInputTextParam, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioOutputText, - InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudioEvalItemInputImage, - ResponseInputAudioParam, -] - InputContent: TypeAlias = Union[ str, ResponseInputTextParam, InputContentOutputText, - InputContentEvalItemInputImage, + InputContentInputImage, ResponseInputAudioParam, - SequenceNotStr[InputContentAnArrayOfInputTextOutputTextInputImageAndInputAudio], + GraderInputsParam, ] From 9688ac0a47ea5083d7b08c2b22186b1232d8a87f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 05:07:08 +0000 Subject: [PATCH 186/408] release: 2.12.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e6c39adb99..0746cbe20a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.11.0" + ".": "2.12.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0878433f..ec61efaf79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 2.12.0 (2025-12-15) + +Full Changelog: [v2.11.0...v2.12.0](https://github.com/openai/openai-python/compare/v2.11.0...v2.12.0) + +### Features + +* **api:** api update ([a95c4d0](https://github.com/openai/openai-python/commit/a95c4d0952ff5eb767206574e687cb029a49a4ab)) +* **api:** fix grader input list, add dated slugs for sora-2 ([b2c389b](https://github.com/openai/openai-python/commit/b2c389bf5c3bde50bac2d9f60cce58f4aef44a41)) + ## 2.11.0 (2025-12-11) Full Changelog: [v2.10.0...v2.11.0](https://github.com/openai/openai-python/compare/v2.10.0...v2.11.0) diff --git a/pyproject.toml b/pyproject.toml index f4e5bdb208..7d6fec5c1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.11.0" +version = "2.12.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 16036c3b6a..9d853d6512 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.11.0" # x-release-please-version +__version__ = "2.12.0" # x-release-please-version From 445d6beb8eff696b513401d6e5537c372d34dd28 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 17:42:34 +0000 Subject: [PATCH 187/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 2d7e7248c7..3793c0f01b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fe8e67bdc351a518b113ab48e775750190e207807903d6b03ab22c438c38a588.yml openapi_spec_hash: 8af972190647ffb9dcec516e19d8761a -config_hash: 856bee50ee3617e85a9bc9274db01dbb +config_hash: d013f4fdd4dd59c6f376a9ca482b7f9e From 3c016c6d943e627e9135a67c5bb2fc92d423e9a1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:18:24 +0000 Subject: [PATCH 188/408] chore(internal): add missing files argument to base client --- src/openai/_base_client.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 58490e4430..c0cac43daa 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1264,9 +1264,12 @@ def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + ) return self.request(cast_to, opts) def put( @@ -1799,9 +1802,12 @@ async def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + ) return await self.request(cast_to, opts) async def put( From 74b1e6f9b9e14a923c63b9681eda6af635207391 Mon Sep 17 00:00:00 2001 From: cameron-stainless Date: Tue, 16 Dec 2025 10:28:21 -0500 Subject: [PATCH 189/408] chore(ci): add CI job to detect breaking changes with the Agents SDK (#1436) * chore: Add CI job to detect breaking changes with agents lib * chore: Make changes based on PR comments * chore: Add newline after each new step --------- Co-authored-by: Cameron McAteer --- .github/workflows/detect-breaking-changes.yml | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index f10fdf3b19..6626d1c376 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -39,4 +39,52 @@ jobs: # Try to check out previous versions of the breaking change detection script. This ensures that # we still detect breaking changes when entire files and their tests are removed. git checkout "${{ github.event.pull_request.base.sha }}" -- ./scripts/detect-breaking-changes 2>/dev/null || true - ./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }} \ No newline at end of file + ./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }} + + agents_sdk: + runs-on: 'ubuntu-latest' + name: Detect Agents SDK regressions + if: github.repository == 'openai/openai-python' + steps: + # Setup this sdk + - uses: actions/checkout@v4 + with: + path: openai-python + + - name: Install Rye + working-directory: openai-python + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + working-directory: openai-python + run: | + rye sync --all-features + + # Setup the agents lib + - uses: actions/checkout@v4 + with: + repository: openai/openai-agents-python + path: openai-agents-python + + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Link to local SDK + working-directory: openai-agents-python + run: uv add ../openai-python + + - name: Install dependencies + working-directory: openai-agents-python + run: make sync + + - name: Run integration type checks + working-directory: openai-agents-python + run: make mypy + From 9dc1d1a080b85571fe80bc48885c85fff3fec305 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 18:13:42 +0000 Subject: [PATCH 190/408] feat(api): gpt-image-1.5 --- .stats.yml | 4 +- src/openai/resources/images.py | 432 +++++++++--------- src/openai/types/image.py | 8 +- .../types/image_edit_completed_event.py | 9 +- src/openai/types/image_edit_params.py | 31 +- src/openai/types/image_gen_completed_event.py | 9 +- src/openai/types/image_generate_params.py | 40 +- src/openai/types/image_model.py | 2 +- src/openai/types/images_response.py | 15 +- src/openai/types/responses/tool.py | 4 +- src/openai/types/responses/tool_param.py | 4 +- 11 files changed, 298 insertions(+), 260 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3793c0f01b..d9366ce3b3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fe8e67bdc351a518b113ab48e775750190e207807903d6b03ab22c438c38a588.yml -openapi_spec_hash: 8af972190647ffb9dcec516e19d8761a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-cded37ac004364c2110ebdacf922ef611b3c51258790c72ca479dcfad4df66aa.yml +openapi_spec_hash: 6e615d34cf8c6bc76e0c6933fc8569af config_hash: d013f4fdd4dd59c6f376a9ca482b7f9e diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 265be6f743..79505a8269 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -151,19 +151,20 @@ def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and + `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + characters for `dall-e-2`, and 32000 characters for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -178,18 +179,18 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are - supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` - is used. + model: The model to use for image generation. Only `dall-e-2` and the GPT image models + are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT + image models is used. n: The number of images to generate. Must be between 1 and 10. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. partial_images: The number of partial images to generate. This parameter is used for streaming @@ -200,17 +201,17 @@ def edit( are generated if the full image is generated more quickly. quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. - Defaults to `auto`. + only supported for the GPT image models. `dall-e-2` only supports `standard` + quality. Defaults to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` - will always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2`, as the GPT image + models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) @@ -264,23 +265,24 @@ def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and + `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + characters for `dall-e-2`, and 32000 characters for the GPT image models. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -295,18 +297,18 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are - supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` - is used. + model: The model to use for image generation. Only `dall-e-2` and the GPT image models + are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT + image models is used. n: The number of images to generate. Must be between 1 and 10. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. partial_images: The number of partial images to generate. This parameter is used for streaming @@ -317,17 +319,17 @@ def edit( are generated if the full image is generated more quickly. quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. - Defaults to `auto`. + only supported for the GPT image models. `dall-e-2` only supports `standard` + quality. Defaults to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` - will always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2`, as the GPT image + models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -377,23 +379,24 @@ def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and + `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + characters for `dall-e-2`, and 32000 characters for the GPT image models. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -408,18 +411,18 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are - supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` - is used. + model: The model to use for image generation. Only `dall-e-2` and the GPT image models + are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT + image models is used. n: The number of images to generate. Must be between 1 and 10. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. partial_images: The number of partial images to generate. This parameter is used for streaming @@ -430,17 +433,17 @@ def edit( are generated if the full image is generated more quickly. quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. - Defaults to `auto`. + only supported for the GPT image models. `dall-e-2` only supports `standard` + quality. Defaults to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` - will always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2`, as the GPT image + models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -555,33 +558,34 @@ def generate( Args: prompt: A text description of the desired image(s). The maximum length is 32000 - characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters - for `dall-e-3`. + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or - `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to - `gpt-image-1` is used. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to + `dall-e-2` unless a parameter specific to the GPT image models is used. - moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must - be either `low` for less restrictive filtering or `auto` (default value). + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. partial_images: The number of partial images to generate. This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to @@ -594,23 +598,23 @@ def generate( - `auto` (default value) will automatically select the best quality for the given model. - - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `high`, `medium` and `low` are supported for the GPT image models. - `hd` and `standard` are supported for `dall-e-3`. - `standard` is the only option for `dall-e-2`. response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes - after the image has been generated. This parameter isn't supported for - `gpt-image-1` which will always return base64-encoded images. + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and - one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of + `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -665,37 +669,38 @@ def generate( Args: prompt: A text description of the desired image(s). The maximum length is 32000 - characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters - for `dall-e-3`. + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or - `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to - `gpt-image-1` is used. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to + `dall-e-2` unless a parameter specific to the GPT image models is used. - moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must - be either `low` for less restrictive filtering or `auto` (default value). + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. partial_images: The number of partial images to generate. This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to @@ -708,19 +713,19 @@ def generate( - `auto` (default value) will automatically select the best quality for the given model. - - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `high`, `medium` and `low` are supported for the GPT image models. - `hd` and `standard` are supported for `dall-e-3`. - `standard` is the only option for `dall-e-2`. response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes - after the image has been generated. This parameter isn't supported for - `gpt-image-1` which will always return base64-encoded images. + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and - one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of + `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -775,37 +780,38 @@ def generate( Args: prompt: A text description of the desired image(s). The maximum length is 32000 - characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters - for `dall-e-3`. + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or - `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to - `gpt-image-1` is used. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to + `dall-e-2` unless a parameter specific to the GPT image models is used. - moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must - be either `low` for less restrictive filtering or `auto` (default value). + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. partial_images: The number of partial images to generate. This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to @@ -818,19 +824,19 @@ def generate( - `auto` (default value) will automatically select the best quality for the given model. - - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `high`, `medium` and `low` are supported for the GPT image models. - `hd` and `standard` are supported for `dall-e-3`. - `standard` is the only option for `dall-e-2`. response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes - after the image has been generated. This parameter isn't supported for - `gpt-image-1` which will always return base64-encoded images. + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and - one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of + `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -1038,19 +1044,20 @@ async def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and + `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + characters for `dall-e-2`, and 32000 characters for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1065,18 +1072,18 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are - supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` - is used. + model: The model to use for image generation. Only `dall-e-2` and the GPT image models + are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT + image models is used. n: The number of images to generate. Must be between 1 and 10. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. partial_images: The number of partial images to generate. This parameter is used for streaming @@ -1087,17 +1094,17 @@ async def edit( are generated if the full image is generated more quickly. quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. - Defaults to `auto`. + only supported for the GPT image models. `dall-e-2` only supports `standard` + quality. Defaults to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` - will always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2`, as the GPT image + models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) @@ -1151,23 +1158,24 @@ async def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and + `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + characters for `dall-e-2`, and 32000 characters for the GPT image models. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1182,18 +1190,18 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are - supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` - is used. + model: The model to use for image generation. Only `dall-e-2` and the GPT image models + are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT + image models is used. n: The number of images to generate. Must be between 1 and 10. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. partial_images: The number of partial images to generate. This parameter is used for streaming @@ -1204,17 +1212,17 @@ async def edit( are generated if the full image is generated more quickly. quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. - Defaults to `auto`. + only supported for the GPT image models. `dall-e-2` only supports `standard` + quality. Defaults to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` - will always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2`, as the GPT image + models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -1264,23 +1272,24 @@ async def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and + `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. prompt: A text description of the desired image(s). The maximum length is 1000 - characters for `dall-e-2`, and 32000 characters for `gpt-image-1`. + characters for `dall-e-2`, and 32000 characters for the GPT image models. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1295,18 +1304,18 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and `gpt-image-1` are - supported. Defaults to `dall-e-2` unless a parameter specific to `gpt-image-1` - is used. + model: The model to use for image generation. Only `dall-e-2` and the GPT image models + are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT + image models is used. n: The number of images to generate. Must be between 1 and 10. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. The + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. partial_images: The number of partial images to generate. This parameter is used for streaming @@ -1317,17 +1326,17 @@ async def edit( are generated if the full image is generated more quickly. quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. - Defaults to `auto`. + only supported for the GPT image models. `dall-e-2` only supports `standard` + quality. Defaults to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as `gpt-image-1` - will always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2`, as the GPT image + models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -1442,33 +1451,34 @@ async def generate( Args: prompt: A text description of the desired image(s). The maximum length is 32000 - characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters - for `dall-e-3`. + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or - `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to - `gpt-image-1` is used. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to + `dall-e-2` unless a parameter specific to the GPT image models is used. - moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must - be either `low` for less restrictive filtering or `auto` (default value). + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. partial_images: The number of partial images to generate. This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to @@ -1481,23 +1491,23 @@ async def generate( - `auto` (default value) will automatically select the best quality for the given model. - - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `high`, `medium` and `low` are supported for the GPT image models. - `hd` and `standard` are supported for `dall-e-3`. - `standard` is the only option for `dall-e-2`. response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes - after the image has been generated. This parameter isn't supported for - `gpt-image-1` which will always return base64-encoded images. + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and - one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of + `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -1552,37 +1562,38 @@ async def generate( Args: prompt: A text description of the desired image(s). The maximum length is 32000 - characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters - for `dall-e-3`. + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or - `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to - `gpt-image-1` is used. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to + `dall-e-2` unless a parameter specific to the GPT image models is used. - moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must - be either `low` for less restrictive filtering or `auto` (default value). + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. partial_images: The number of partial images to generate. This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to @@ -1595,19 +1606,19 @@ async def generate( - `auto` (default value) will automatically select the best quality for the given model. - - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `high`, `medium` and `low` are supported for the GPT image models. - `hd` and `standard` are supported for `dall-e-3`. - `standard` is the only option for `dall-e-2`. response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes - after the image has been generated. This parameter isn't supported for - `gpt-image-1` which will always return base64-encoded images. + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and - one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of + `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -1662,37 +1673,38 @@ async def generate( Args: prompt: A text description of the desired image(s). The maximum length is 32000 - characters for `gpt-image-1`, 1000 characters for `dall-e-2` and 4000 characters - for `dall-e-3`. + characters for the GPT image models, 1000 characters for `dall-e-2` and 4000 + characters for `dall-e-3`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. - model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or - `gpt-image-1`. Defaults to `dall-e-2` unless a parameter specific to - `gpt-image-1` is used. + model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to + `dall-e-2` unless a parameter specific to the GPT image models is used. - moderation: Control the content-moderation level for images generated by `gpt-image-1`. Must - be either `low` for less restrictive filtering or `auto` (default value). + moderation: Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported. output_compression: The compression level (0-100%) for the generated images. This parameter is only - supported for `gpt-image-1` with the `webp` or `jpeg` output formats, and + supported for the GPT image models with the `webp` or `jpeg` output formats, and defaults to 100. output_format: The format in which the generated images are returned. This parameter is only - supported for `gpt-image-1`. Must be one of `png`, `jpeg`, or `webp`. + supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. partial_images: The number of partial images to generate. This parameter is used for streaming responses that return partial images. Value must be between 0 and 3. When set to @@ -1705,19 +1717,19 @@ async def generate( - `auto` (default value) will automatically select the best quality for the given model. - - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `high`, `medium` and `low` are supported for the GPT image models. - `hd` and `standard` are supported for `dall-e-3`. - `standard` is the only option for `dall-e-2`. response_format: The format in which generated images with `dall-e-2` and `dall-e-3` are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes - after the image has been generated. This parameter isn't supported for - `gpt-image-1` which will always return base64-encoded images. + after the image has been generated. This parameter isn't supported for the GPT + image models, which always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for - `gpt-image-1`, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and - one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image + models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of + `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean diff --git a/src/openai/types/image.py b/src/openai/types/image.py index 9e2a23fa40..dcbdb2aceb 100644 --- a/src/openai/types/image.py +++ b/src/openai/types/image.py @@ -13,8 +13,8 @@ class Image(BaseModel): b64_json: Optional[str] = None """The base64-encoded JSON of the generated image. - Default value for `gpt-image-1`, and only present if `response_format` is set to - `b64_json` for `dall-e-2` and `dall-e-3`. + Returned by default for the GPT image models, and only present if + `response_format` is set to `b64_json` for `dall-e-2` and `dall-e-3`. """ revised_prompt: Optional[str] = None @@ -23,6 +23,6 @@ class Image(BaseModel): url: Optional[str] = None """ When using `dall-e-2` or `dall-e-3`, the URL of the generated image if - `response_format` is set to `url` (default value). Unsupported for - `gpt-image-1`. + `response_format` is set to `url` (default value). Unsupported for the GPT image + models. """ diff --git a/src/openai/types/image_edit_completed_event.py b/src/openai/types/image_edit_completed_event.py index 5bd2986d2a..e2e193413f 100644 --- a/src/openai/types/image_edit_completed_event.py +++ b/src/openai/types/image_edit_completed_event.py @@ -18,7 +18,9 @@ class UsageInputTokensDetails(BaseModel): class Usage(BaseModel): - """For `gpt-image-1` only, the token usage information for the image generation.""" + """ + For the GPT image models only, the token usage information for the image generation. + """ input_tokens: int """The number of tokens (images and text) in the input prompt.""" @@ -58,4 +60,7 @@ class ImageEditCompletedEvent(BaseModel): """The type of the event. Always `image_edit.completed`.""" usage: Usage - """For `gpt-image-1` only, the token usage information for the image generation.""" + """ + For the GPT image models only, the token usage information for the image + generation. + """ diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 2a8fab0f20..0bd5f39fac 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -15,7 +15,8 @@ class ImageEditParamsBase(TypedDict, total=False): image: Required[Union[FileTypes, SequenceNotStr[FileTypes]]] """The image(s) to edit. Must be a supported image file or an array of images. - For `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less than + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and + `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` @@ -26,15 +27,15 @@ class ImageEditParamsBase(TypedDict, total=False): """A text description of the desired image(s). The maximum length is 1000 characters for `dall-e-2`, and 32000 characters for - `gpt-image-1`. + the GPT image models. """ background: Optional[Literal["transparent", "opaque", "auto"]] """ Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -59,8 +60,8 @@ class ImageEditParamsBase(TypedDict, total=False): model: Union[str, ImageModel, None] """The model to use for image generation. - Only `dall-e-2` and `gpt-image-1` are supported. Defaults to `dall-e-2` unless a - parameter specific to `gpt-image-1` is used. + Only `dall-e-2` and the GPT image models are supported. Defaults to `dall-e-2` + unless a parameter specific to the GPT image models is used. """ n: Optional[int] @@ -69,14 +70,14 @@ class ImageEditParamsBase(TypedDict, total=False): output_compression: Optional[int] """The compression level (0-100%) for the generated images. - This parameter is only supported for `gpt-image-1` with the `webp` or `jpeg` - output formats, and defaults to 100. + This parameter is only supported for the GPT image models with the `webp` or + `jpeg` output formats, and defaults to 100. """ output_format: Optional[Literal["png", "jpeg", "webp"]] """The format in which the generated images are returned. - This parameter is only supported for `gpt-image-1`. Must be one of `png`, + This parameter is only supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. The default value is `png`. """ @@ -94,8 +95,8 @@ class ImageEditParamsBase(TypedDict, total=False): quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] """The quality of the image that will be generated. - `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only - supports `standard` quality. Defaults to `auto`. + `high`, `medium` and `low` are only supported for the GPT image models. + `dall-e-2` only supports `standard` quality. Defaults to `auto`. """ response_format: Optional[Literal["url", "b64_json"]] @@ -103,15 +104,15 @@ class ImageEditParamsBase(TypedDict, total=False): Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been generated. This parameter is only supported for `dall-e-2`, as - `gpt-image-1` will always return base64-encoded images. + the GPT image models always return base64-encoded images. """ size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] """The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or - `auto` (default value) for `gpt-image-1`, and one of `256x256`, `512x512`, or - `1024x1024` for `dall-e-2`. + `auto` (default value) for the GPT image models, and one of `256x256`, + `512x512`, or `1024x1024` for `dall-e-2`. """ user: str diff --git a/src/openai/types/image_gen_completed_event.py b/src/openai/types/image_gen_completed_event.py index dc9ccb8cfc..813ed889d8 100644 --- a/src/openai/types/image_gen_completed_event.py +++ b/src/openai/types/image_gen_completed_event.py @@ -18,7 +18,9 @@ class UsageInputTokensDetails(BaseModel): class Usage(BaseModel): - """For `gpt-image-1` only, the token usage information for the image generation.""" + """ + For the GPT image models only, the token usage information for the image generation. + """ input_tokens: int """The number of tokens (images and text) in the input prompt.""" @@ -58,4 +60,7 @@ class ImageGenCompletedEvent(BaseModel): """The type of the event. Always `image_generation.completed`.""" usage: Usage - """For `gpt-image-1` only, the token usage information for the image generation.""" + """ + For the GPT image models only, the token usage information for the image + generation. + """ diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 3270ca1d6e..7a95b3dd3d 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -14,16 +14,16 @@ class ImageGenerateParamsBase(TypedDict, total=False): prompt: Required[str] """A text description of the desired image(s). - The maximum length is 32000 characters for `gpt-image-1`, 1000 characters for - `dall-e-2` and 4000 characters for `dall-e-3`. + The maximum length is 32000 characters for the GPT image models, 1000 characters + for `dall-e-2` and 4000 characters for `dall-e-3`. """ background: Optional[Literal["transparent", "opaque", "auto"]] """ Allows to set transparency for the background of the generated image(s). This - parameter is only supported for `gpt-image-1`. Must be one of `transparent`, - `opaque` or `auto` (default value). When `auto` is used, the model will - automatically determine the best background for the image. + parameter is only supported for the GPT image models. Must be one of + `transparent`, `opaque` or `auto` (default value). When `auto` is used, the + model will automatically determine the best background for the image. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -32,14 +32,16 @@ class ImageGenerateParamsBase(TypedDict, total=False): model: Union[str, ImageModel, None] """The model to use for image generation. - One of `dall-e-2`, `dall-e-3`, or `gpt-image-1`. Defaults to `dall-e-2` unless a - parameter specific to `gpt-image-1` is used. + One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`, + `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to `dall-e-2` unless a parameter + specific to the GPT image models is used. """ moderation: Optional[Literal["low", "auto"]] - """Control the content-moderation level for images generated by `gpt-image-1`. - - Must be either `low` for less restrictive filtering or `auto` (default value). + """ + Control the content-moderation level for images generated by the GPT image + models. Must be either `low` for less restrictive filtering or `auto` (default + value). """ n: Optional[int] @@ -51,14 +53,14 @@ class ImageGenerateParamsBase(TypedDict, total=False): output_compression: Optional[int] """The compression level (0-100%) for the generated images. - This parameter is only supported for `gpt-image-1` with the `webp` or `jpeg` - output formats, and defaults to 100. + This parameter is only supported for the GPT image models with the `webp` or + `jpeg` output formats, and defaults to 100. """ output_format: Optional[Literal["png", "jpeg", "webp"]] """The format in which the generated images are returned. - This parameter is only supported for `gpt-image-1`. Must be one of `png`, + This parameter is only supported for the GPT image models. Must be one of `png`, `jpeg`, or `webp`. """ @@ -78,7 +80,7 @@ class ImageGenerateParamsBase(TypedDict, total=False): - `auto` (default value) will automatically select the best quality for the given model. - - `high`, `medium` and `low` are supported for `gpt-image-1`. + - `high`, `medium` and `low` are supported for the GPT image models. - `hd` and `standard` are supported for `dall-e-3`. - `standard` is the only option for `dall-e-2`. """ @@ -88,8 +90,8 @@ class ImageGenerateParamsBase(TypedDict, total=False): returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the - image has been generated. This parameter isn't supported for `gpt-image-1` which - will always return base64-encoded images. + image has been generated. This parameter isn't supported for the GPT image + models, which always return base64-encoded images. """ size: Optional[ @@ -98,7 +100,7 @@ class ImageGenerateParamsBase(TypedDict, total=False): """The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or - `auto` (default value) for `gpt-image-1`, one of `256x256`, `512x512`, or + `auto` (default value) for the GPT image models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. """ @@ -126,7 +128,7 @@ class ImageGenerateParamsNonStreaming(ImageGenerateParamsBase, total=False): Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. """ @@ -136,7 +138,7 @@ class ImageGenerateParamsStreaming(ImageGenerateParamsBase): Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) - for more information. This parameter is only supported for `gpt-image-1`. + for more information. This parameter is only supported for the GPT image models. """ diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py index 22b1281fa9..8ea486fbb6 100644 --- a/src/openai/types/image_model.py +++ b/src/openai/types/image_model.py @@ -4,4 +4,4 @@ __all__ = ["ImageModel"] -ImageModel: TypeAlias = Literal["dall-e-2", "dall-e-3", "gpt-image-1", "gpt-image-1-mini"] +ImageModel: TypeAlias = Literal["gpt-image-1.5", "dall-e-2", "dall-e-3", "gpt-image-1", "gpt-image-1-mini"] diff --git a/src/openai/types/images_response.py b/src/openai/types/images_response.py index 914017823e..3e832aadf2 100644 --- a/src/openai/types/images_response.py +++ b/src/openai/types/images_response.py @@ -6,7 +6,7 @@ from .image import Image from .._models import BaseModel -__all__ = ["ImagesResponse", "Usage", "UsageInputTokensDetails"] +__all__ = ["ImagesResponse", "Usage", "UsageInputTokensDetails", "UsageOutputTokensDetails"] class UsageInputTokensDetails(BaseModel): @@ -19,6 +19,16 @@ class UsageInputTokensDetails(BaseModel): """The number of text tokens in the input prompt.""" +class UsageOutputTokensDetails(BaseModel): + """The output token details for the image generation.""" + + image_tokens: int + """The number of image output tokens generated by the model.""" + + text_tokens: int + """The number of text output tokens generated by the model.""" + + class Usage(BaseModel): """For `gpt-image-1` only, the token usage information for the image generation.""" @@ -34,6 +44,9 @@ class Usage(BaseModel): total_tokens: int """The total number of tokens (images and text) used for the image generation.""" + output_tokens_details: Optional[UsageOutputTokensDetails] = None + """The output token details for the image generation.""" + class ImagesResponse(BaseModel): """The response from the image generation endpoint.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 1f1ef12358..20f50e4478 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -221,7 +221,7 @@ class ImageGenerationInputImageMask(BaseModel): class ImageGeneration(BaseModel): - """A tool that generates images using a model like `gpt-image-1`.""" + """A tool that generates images using the GPT image models.""" type: Literal["image_generation"] """The type of the image generation tool. Always `image_generation`.""" @@ -246,7 +246,7 @@ class ImageGeneration(BaseModel): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Optional[Literal["gpt-image-1", "gpt-image-1-mini"]] = None + model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini"], None] = None """The image generation model to use. Default: `gpt-image-1`.""" moderation: Optional[Literal["auto", "low"]] = None diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index c6ffa39192..69c6162153 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -221,7 +221,7 @@ class ImageGenerationInputImageMask(TypedDict, total=False): class ImageGeneration(TypedDict, total=False): - """A tool that generates images using a model like `gpt-image-1`.""" + """A tool that generates images using the GPT image models.""" type: Required[Literal["image_generation"]] """The type of the image generation tool. Always `image_generation`.""" @@ -246,7 +246,7 @@ class ImageGeneration(TypedDict, total=False): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Literal["gpt-image-1", "gpt-image-1-mini"] + model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini"]] """The image generation model to use. Default: `gpt-image-1`.""" moderation: Literal["auto", "low"] From f94256daf8f5d7268f28c2330dc343660a959c3e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 18:14:52 +0000 Subject: [PATCH 191/408] release: 2.13.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0746cbe20a..e6eadb43e0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.12.0" + ".": "2.13.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ec61efaf79..745512f15d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 2.13.0 (2025-12-16) + +Full Changelog: [v2.12.0...v2.13.0](https://github.com/openai/openai-python/compare/v2.12.0...v2.13.0) + +### Features + +* **api:** gpt-image-1.5 ([1c88f03](https://github.com/openai/openai-python/commit/1c88f03bb48aa67426744e5b74f6197f30f61c73)) + + +### Chores + +* **ci:** add CI job to detect breaking changes with the Agents SDK ([#1436](https://github.com/openai/openai-python/issues/1436)) ([237c91e](https://github.com/openai/openai-python/commit/237c91ee6738b6764b139fd7afa68294d3ee0153)) +* **internal:** add missing files argument to base client ([e6d6fd5](https://github.com/openai/openai-python/commit/e6d6fd5989d76358ea5d9abb5949aa87646cbef6)) + ## 2.12.0 (2025-12-15) Full Changelog: [v2.11.0...v2.12.0](https://github.com/openai/openai-python/compare/v2.11.0...v2.12.0) diff --git a/pyproject.toml b/pyproject.toml index 7d6fec5c1a..f9dd23592a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.12.0" +version = "2.13.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 9d853d6512..dfda2f0522 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.12.0" # x-release-please-version +__version__ = "2.13.0" # x-release-please-version From 20af6aaae2a7ec066fc64922d314dba693526ca1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:44:31 +0000 Subject: [PATCH 192/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index d9366ce3b3..d44b98a331 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-cded37ac004364c2110ebdacf922ef611b3c51258790c72ca479dcfad4df66aa.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-235aa1c75c6cc178b97d074a4671343469f458c4a306ef7beb4e45ab252aa589.yml openapi_spec_hash: 6e615d34cf8c6bc76e0c6933fc8569af -config_hash: d013f4fdd4dd59c6f376a9ca482b7f9e +config_hash: c028ce402ef5f71da947c3f15bf6046d From 62699d97ba171ec109456e3001ecd693e6945060 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 15:47:28 +0000 Subject: [PATCH 193/408] fix: use async_to_httpx_files in patch method --- src/openai/_base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index c0cac43daa..9e536410d6 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1806,7 +1806,7 @@ async def patch( options: RequestOptions = {}, ) -> ResponseT: opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) From 51c6885537da15fa2fc7e93ec9a4166ad9928f77 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 18:45:19 +0000 Subject: [PATCH 194/408] feat(api): slugs for new audio models; make all `model` params accept strings --- .stats.yml | 4 +- src/openai/resources/audio/speech.py | 4 +- src/openai/resources/audio/transcriptions.py | 65 ++++++++++--------- src/openai/resources/realtime/calls.py | 4 ++ src/openai/resources/videos.py | 11 ++-- src/openai/types/__init__.py | 1 + .../types/audio/speech_create_params.py | 2 +- src/openai/types/audio/speech_model.py | 2 +- .../audio/transcription_create_params.py | 12 ++-- src/openai/types/audio_model.py | 8 ++- .../types/realtime/audio_transcription.py | 19 ++++-- .../realtime/audio_transcription_param.py | 17 ++++- .../types/realtime/call_accept_params.py | 2 + .../realtime_session_create_request.py | 2 + .../realtime_session_create_request_param.py | 2 + .../realtime_session_create_response.py | 2 + src/openai/types/video_create_params.py | 4 +- src/openai/types/video_model.py | 5 +- src/openai/types/video_model_param.py | 12 ++++ tests/api_resources/realtime/test_calls.py | 8 +-- .../realtime/test_client_secrets.py | 4 +- tests/api_resources/test_videos.py | 4 +- 22 files changed, 126 insertions(+), 68 deletions(-) create mode 100644 src/openai/types/video_model_param.py diff --git a/.stats.yml b/.stats.yml index d44b98a331..6ec100546a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-235aa1c75c6cc178b97d074a4671343469f458c4a306ef7beb4e45ab252aa589.yml -openapi_spec_hash: 6e615d34cf8c6bc76e0c6933fc8569af +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-75926226b642ebb2cb415694da9dff35e8ab40145ac1b791cefb82a83809db4d.yml +openapi_spec_hash: 6a0e391b0ba5747b6b4a3e5fe21de4da config_hash: c028ce402ef5f71da947c3f15bf6046d diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 992fb5971a..b92fcb73d3 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -72,7 +72,7 @@ def create( model: One of the available [TTS models](https://platform.openai.com/docs/models#tts): - `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. + `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and @@ -168,7 +168,7 @@ async def create( model: One of the available [TTS models](https://platform.openai.com/docs/models#tts): - `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. + `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index a5c86146d4..599534855d 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -91,8 +91,9 @@ def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source - Whisper V2 model). + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server first normalizes loudness and then uses voice activity detection (VAD) to choose @@ -102,8 +103,9 @@ def create( include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. language: The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) @@ -239,8 +241,9 @@ def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source - Whisper V2 model), and `gpt-4o-transcribe-diarize`. + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -261,9 +264,9 @@ def create( include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. This field is not supported when using - `gpt-4o-transcribe-diarize`. + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for @@ -346,8 +349,9 @@ def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source - Whisper V2 model), and `gpt-4o-transcribe-diarize`. + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -368,9 +372,9 @@ def create( include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. This field is not supported when using - `gpt-4o-transcribe-diarize`. + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for @@ -535,8 +539,9 @@ async def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source - Whisper V2 model), and `gpt-4o-transcribe-diarize`. + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. chunking_strategy: Controls how the audio is cut into chunks. When set to `"auto"`, the server first normalizes loudness and then uses voice activity detection (VAD) to choose @@ -548,9 +553,9 @@ async def create( include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. This field is not supported when using - `gpt-4o-transcribe-diarize`. + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for @@ -679,8 +684,9 @@ async def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source - Whisper V2 model), and `gpt-4o-transcribe-diarize`. + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -701,9 +707,9 @@ async def create( include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. This field is not supported when using - `gpt-4o-transcribe-diarize`. + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for @@ -786,8 +792,9 @@ async def create( flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm. model: ID of the model to use. The options are `gpt-4o-transcribe`, - `gpt-4o-mini-transcribe`, `whisper-1` (which is powered by our open source - Whisper V2 model), and `gpt-4o-transcribe-diarize`. + `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` + (which is powered by our open source Whisper V2 model), and + `gpt-4o-transcribe-diarize`. stream: If set to true, the model response data will be streamed to the client as it is generated using @@ -808,9 +815,9 @@ async def create( include: Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. This field is not supported when using - `gpt-4o-transcribe-diarize`. + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. known_speaker_names: Optional list of speaker names that correspond to the audio samples provided in `known_speaker_references[]`. Each entry should be a short identifier (for diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index cdea492d95..20a22fc3b6 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -125,8 +125,10 @@ def accept( "gpt-4o-mini-realtime-preview-2024-12-17", "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", ], ] | Omit = omit, @@ -450,8 +452,10 @@ async def accept( "gpt-4o-mini-realtime-preview-2024-12-17", "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", ], ] | Omit = omit, diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 727091c607..9f74c942bc 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -10,7 +10,6 @@ from .. import _legacy_response from ..types import ( VideoSize, - VideoModel, VideoSeconds, video_list_params, video_remix_params, @@ -34,8 +33,8 @@ from .._base_client import AsyncPaginator, make_request_options from .._utils._utils import is_given from ..types.video_size import VideoSize -from ..types.video_model import VideoModel from ..types.video_seconds import VideoSeconds +from ..types.video_model_param import VideoModelParam from ..types.video_delete_response import VideoDeleteResponse __all__ = ["Videos", "AsyncVideos"] @@ -66,7 +65,7 @@ def create( *, prompt: str, input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, + model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -130,7 +129,7 @@ def create_and_poll( *, prompt: str, input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, + model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, poll_interval_ms: int | Omit = omit, @@ -421,7 +420,7 @@ async def create( *, prompt: str, input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, + model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -485,7 +484,7 @@ async def create_and_poll( *, prompt: str, input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, + model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, poll_interval_ms: int | Omit = omit, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index a98ca16ee9..5eb267e845 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -53,6 +53,7 @@ from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams from .video_list_params import VideoListParams as VideoListParams +from .video_model_param import VideoModelParam as VideoModelParam from .eval_create_params import EvalCreateParams as EvalCreateParams from .eval_list_response import EvalListResponse as EvalListResponse from .eval_update_params import EvalUpdateParams as EvalUpdateParams diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 634d788191..37180995c8 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -17,7 +17,7 @@ class SpeechCreateParams(TypedDict, total=False): model: Required[Union[str, SpeechModel]] """ One of the available [TTS models](https://platform.openai.com/docs/models#tts): - `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`. + `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. """ voice: Required[ diff --git a/src/openai/types/audio/speech_model.py b/src/openai/types/audio/speech_model.py index f004f805da..31294a05b6 100644 --- a/src/openai/types/audio/speech_model.py +++ b/src/openai/types/audio/speech_model.py @@ -4,4 +4,4 @@ __all__ = ["SpeechModel"] -SpeechModel: TypeAlias = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts"] +SpeechModel: TypeAlias = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts", "gpt-4o-mini-tts-2025-12-15"] diff --git a/src/openai/types/audio/transcription_create_params.py b/src/openai/types/audio/transcription_create_params.py index adaef9f5fe..15540fc73f 100644 --- a/src/openai/types/audio/transcription_create_params.py +++ b/src/openai/types/audio/transcription_create_params.py @@ -29,9 +29,9 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): model: Required[Union[str, AudioModel]] """ID of the model to use. - The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, `whisper-1` - (which is powered by our open source Whisper V2 model), and - `gpt-4o-transcribe-diarize`. + The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, + `gpt-4o-mini-transcribe-2025-12-15`, `whisper-1` (which is powered by our open + source Whisper V2 model), and `gpt-4o-transcribe-diarize`. """ chunking_strategy: Optional[ChunkingStrategy] @@ -49,9 +49,9 @@ class TranscriptionCreateParamsBase(TypedDict, total=False): Additional information to include in the transcription response. `logprobs` will return the log probabilities of the tokens in the response to understand the model's confidence in the transcription. `logprobs` only works with - response_format set to `json` and only with the models `gpt-4o-transcribe` and - `gpt-4o-mini-transcribe`. This field is not supported when using - `gpt-4o-transcribe-diarize`. + response_format set to `json` and only with the models `gpt-4o-transcribe`, + `gpt-4o-mini-transcribe`, and `gpt-4o-mini-transcribe-2025-12-15`. This field is + not supported when using `gpt-4o-transcribe-diarize`. """ known_speaker_names: SequenceNotStr[str] diff --git a/src/openai/types/audio_model.py b/src/openai/types/audio_model.py index 68031a2198..8acada6dc3 100644 --- a/src/openai/types/audio_model.py +++ b/src/openai/types/audio_model.py @@ -4,4 +4,10 @@ __all__ = ["AudioModel"] -AudioModel: TypeAlias = Literal["whisper-1", "gpt-4o-transcribe", "gpt-4o-mini-transcribe", "gpt-4o-transcribe-diarize"] +AudioModel: TypeAlias = Literal[ + "whisper-1", + "gpt-4o-transcribe", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe-diarize", +] diff --git a/src/openai/types/realtime/audio_transcription.py b/src/openai/types/realtime/audio_transcription.py index 3e5c8e0cb4..0a8c1371e0 100644 --- a/src/openai/types/realtime/audio_transcription.py +++ b/src/openai/types/realtime/audio_transcription.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Union, Optional from typing_extensions import Literal from ..._models import BaseModel @@ -17,13 +17,22 @@ class AudioTranscription(BaseModel): format will improve accuracy and latency. """ - model: Optional[ - Literal["whisper-1", "gpt-4o-mini-transcribe", "gpt-4o-transcribe", "gpt-4o-transcribe-diarize"] + model: Union[ + str, + Literal[ + "whisper-1", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + ], + None, ] = None """The model to use for transcription. - Current options are `whisper-1`, `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, - and `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need + Current options are `whisper-1`, `gpt-4o-mini-transcribe`, + `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, and + `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need diarization with speaker labels. """ diff --git a/src/openai/types/realtime/audio_transcription_param.py b/src/openai/types/realtime/audio_transcription_param.py index 3b65e42c8f..7e60a003ce 100644 --- a/src/openai/types/realtime/audio_transcription_param.py +++ b/src/openai/types/realtime/audio_transcription_param.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Union from typing_extensions import Literal, TypedDict __all__ = ["AudioTranscriptionParam"] @@ -16,11 +17,21 @@ class AudioTranscriptionParam(TypedDict, total=False): format will improve accuracy and latency. """ - model: Literal["whisper-1", "gpt-4o-mini-transcribe", "gpt-4o-transcribe", "gpt-4o-transcribe-diarize"] + model: Union[ + str, + Literal[ + "whisper-1", + "gpt-4o-mini-transcribe", + "gpt-4o-mini-transcribe-2025-12-15", + "gpt-4o-transcribe", + "gpt-4o-transcribe-diarize", + ], + ] """The model to use for transcription. - Current options are `whisper-1`, `gpt-4o-mini-transcribe`, `gpt-4o-transcribe`, - and `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need + Current options are `whisper-1`, `gpt-4o-mini-transcribe`, + `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, and + `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need diarization with speaker labels. """ diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index 917b71cb0d..d950f59f69 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -65,8 +65,10 @@ class CallAcceptParams(TypedDict, total=False): "gpt-4o-mini-realtime-preview-2024-12-17", "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", ], ] """The Realtime model used for this session.""" diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 76738816a0..4a93c91c7d 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -66,8 +66,10 @@ class RealtimeSessionCreateRequest(BaseModel): "gpt-4o-mini-realtime-preview-2024-12-17", "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", ], None, ] = None diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index cc5806fe11..dee63d0967 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -67,8 +67,10 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): "gpt-4o-mini-realtime-preview-2024-12-17", "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", ], ] """The Realtime model used for this session.""" diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 46d32e8571..15a200ca17 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -469,8 +469,10 @@ class RealtimeSessionCreateResponse(BaseModel): "gpt-4o-mini-realtime-preview-2024-12-17", "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", + "gpt-realtime-mini-2025-12-15", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", + "gpt-audio-mini-2025-12-15", ], None, ] = None diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py index c4d3e0851f..d787aaeddd 100644 --- a/src/openai/types/video_create_params.py +++ b/src/openai/types/video_create_params.py @@ -6,8 +6,8 @@ from .._types import FileTypes from .video_size import VideoSize -from .video_model import VideoModel from .video_seconds import VideoSeconds +from .video_model_param import VideoModelParam __all__ = ["VideoCreateParams"] @@ -19,7 +19,7 @@ class VideoCreateParams(TypedDict, total=False): input_reference: FileTypes """Optional image reference that guides generation.""" - model: VideoModel + model: VideoModelParam """The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`. diff --git a/src/openai/types/video_model.py b/src/openai/types/video_model.py index e96e7685f8..29d8cb16eb 100644 --- a/src/openai/types/video_model.py +++ b/src/openai/types/video_model.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Union from typing_extensions import Literal, TypeAlias __all__ = ["VideoModel"] -VideoModel: TypeAlias = Literal[ - "sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-pro-2025-10-06", "sora-2-2025-12-08" +VideoModel: TypeAlias = Union[ + str, Literal["sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-pro-2025-10-06", "sora-2-2025-12-08"] ] diff --git a/src/openai/types/video_model_param.py b/src/openai/types/video_model_param.py new file mode 100644 index 0000000000..4310b8d057 --- /dev/null +++ b/src/openai/types/video_model_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["VideoModelParam"] + +VideoModelParam: TypeAlias = Union[ + str, Literal["sora-2", "sora-2-pro", "sora-2-2025-10-06", "sora-2-pro-2025-10-06", "sora-2-2025-12-08"] +] diff --git a/tests/api_resources/realtime/test_calls.py b/tests/api_resources/realtime/test_calls.py index 5495a58a4e..9bb6ef3faf 100644 --- a/tests/api_resources/realtime/test_calls.py +++ b/tests/api_resources/realtime/test_calls.py @@ -48,7 +48,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "whisper-1", + "model": "string", "prompt": "prompt", }, "turn_detection": { @@ -147,7 +147,7 @@ def test_method_accept_with_all_params(self, client: OpenAI) -> None: "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "whisper-1", + "model": "string", "prompt": "prompt", }, "turn_detection": { @@ -386,7 +386,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "whisper-1", + "model": "string", "prompt": "prompt", }, "turn_detection": { @@ -485,7 +485,7 @@ async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "whisper-1", + "model": "string", "prompt": "prompt", }, "turn_detection": { diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py index cd15b4be52..17762771ac 100644 --- a/tests/api_resources/realtime/test_client_secrets.py +++ b/tests/api_resources/realtime/test_client_secrets.py @@ -40,7 +40,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "whisper-1", + "model": "string", "prompt": "prompt", }, "turn_detection": { @@ -136,7 +136,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "whisper-1", + "model": "string", "prompt": "prompt", }, "turn_detection": { diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py index 623cfc2153..b785e03ca5 100644 --- a/tests/api_resources/test_videos.py +++ b/tests/api_resources/test_videos.py @@ -39,7 +39,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: video = client.videos.create( prompt="x", input_reference=b"raw file contents", - model="sora-2", + model="string", seconds="4", size="720x1280", ) @@ -297,7 +297,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> video = await async_client.videos.create( prompt="x", input_reference=b"raw file contents", - model="sora-2", + model="string", seconds="4", size="720x1280", ) From a3c27a28d8a3b69f43e4722f97b3cc96a5f2171f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 21:52:28 +0000 Subject: [PATCH 195/408] chore(internal): add `--fix` argument to lint script --- scripts/lint | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/lint b/scripts/lint index 55bc1dd711..275a2bf1a1 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,8 +4,13 @@ set -e cd "$(dirname "$0")/.." -echo "==> Running lints" -rye run lint +if [ "$1" = "--fix" ]; then + echo "==> Running lints with --fix" + rye run fix:ruff +else + echo "==> Running lints" + rye run lint +fi echo "==> Making sure it imports" rye run python -c 'import openai' From 4547f1a0880d9765134e385709c9049462d270b1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 03:04:53 +0000 Subject: [PATCH 196/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 6ec100546a..e9485c8224 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-75926226b642ebb2cb415694da9dff35e8ab40145ac1b791cefb82a83809db4d.yml openapi_spec_hash: 6a0e391b0ba5747b6b4a3e5fe21de4da -config_hash: c028ce402ef5f71da947c3f15bf6046d +config_hash: adcf23ecf5f84d3cadf1d71e82ec636a From d3e632171c7842abf97b26379f564531d80ad096 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 03:05:26 +0000 Subject: [PATCH 197/408] release: 2.14.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e6eadb43e0..5868289349 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.13.0" + ".": "2.14.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 745512f15d..ca71bc108e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 2.14.0 (2025-12-19) + +Full Changelog: [v2.13.0...v2.14.0](https://github.com/openai/openai-python/compare/v2.13.0...v2.14.0) + +### Features + +* **api:** slugs for new audio models; make all `model` params accept strings ([e517792](https://github.com/openai/openai-python/commit/e517792b58d1768cfb3432a555a354ae0a9cfa21)) + + +### Bug Fixes + +* use async_to_httpx_files in patch method ([a6af9ee](https://github.com/openai/openai-python/commit/a6af9ee5643197222f328d5e73a80ab3515c32e2)) + + +### Chores + +* **internal:** add `--fix` argument to lint script ([93107ef](https://github.com/openai/openai-python/commit/93107ef36abcfd9c6b1419533a1720031f03caec)) + ## 2.13.0 (2025-12-16) Full Changelog: [v2.12.0...v2.13.0](https://github.com/openai/openai-python/compare/v2.12.0...v2.13.0) diff --git a/pyproject.toml b/pyproject.toml index f9dd23592a..a720293abb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.13.0" +version = "2.14.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index dfda2f0522..5fe6b5b08c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.13.0" # x-release-please-version +__version__ = "2.14.0" # x-release-please-version From 032e6cb557aab92acd17b883f0801746afd315b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 04:06:54 +0000 Subject: [PATCH 198/408] chore(internal): codegen related update --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index f011417af6..cbb5bb26e4 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 OpenAI + Copyright 2026 OpenAI Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 5da8c5f36a3a31050608dbd5de067985275dc176 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 21:53:36 +0000 Subject: [PATCH 199/408] feat(api): add new Response completed_at prop --- .stats.yml | 6 +++--- src/openai/resources/audio/speech.py | 14 ++++++++------ src/openai/resources/images.py | 18 ++++++++++++------ src/openai/resources/responses/input_tokens.py | 8 ++------ src/openai/types/audio/speech_create_params.py | 6 +++--- .../types/chat/chat_completion_audio_param.py | 4 ++-- .../realtime/realtime_audio_config_output.py | 6 +++--- .../realtime_audio_config_output_param.py | 6 +++--- .../realtime_response_create_audio_output.py | 7 +++---- ...ltime_response_create_audio_output_param.py | 7 +++---- .../responses/input_token_count_params.py | 6 +----- src/openai/types/responses/response.py | 16 +++++++++++----- .../responses/response_compaction_item.py | 2 ++ .../response_compaction_item_param.py | 1 + .../response_compaction_item_param_param.py | 1 + ...response_function_shell_tool_call_output.py | 8 ++++++-- .../responses/response_function_web_search.py | 5 ++++- .../response_function_web_search_param.py | 7 ++++++- src/openai/types/responses/tool.py | 1 + src/openai/types/responses/tool_param.py | 1 + src/openai/types/video_create_error.py | 4 ++++ 21 files changed, 80 insertions(+), 54 deletions(-) diff --git a/.stats.yml b/.stats.yml index e9485c8224..fe01e150f9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-75926226b642ebb2cb415694da9dff35e8ab40145ac1b791cefb82a83809db4d.yml -openapi_spec_hash: 6a0e391b0ba5747b6b4a3e5fe21de4da -config_hash: adcf23ecf5f84d3cadf1d71e82ec636a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9442fa9212dd61aac2bb0edd19744bee381e75888712f9098bc6ebb92c52b557.yml +openapi_spec_hash: f87823d164b7a8f72a42eba04e482a99 +config_hash: ad7136f7366fddec432ec378939e58a7 diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index b92fcb73d3..f2c8d635f3 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -74,9 +74,10 @@ def create( One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. - voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, - `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and - `verse`. Previews of the voices are available in the + voice: The voice to use when generating the audio. Supported built-in voices are + `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. Previews of the voices are available + in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). instructions: Control the voice of your generated audio with additional instructions. Does not @@ -170,9 +171,10 @@ async def create( One of the available [TTS models](https://platform.openai.com/docs/models#tts): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. - voice: The voice to use when generating the audio. Supported voices are `alloy`, `ash`, - `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, `shimmer`, and - `verse`. Previews of the voices are available in the + voice: The voice to use when generating the audio. Supported built-in voices are + `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. Previews of the voices are available + in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). instructions: Control the voice of your generated audio with additional instructions. Does not diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 79505a8269..805828488f 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -146,7 +146,8 @@ def edit( """Creates an edited or extended image given one or more source images and a prompt. - This endpoint only supports `gpt-image-1` and `dall-e-2`. + This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, + and `gpt-image-1-mini`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. @@ -260,7 +261,8 @@ def edit( """Creates an edited or extended image given one or more source images and a prompt. - This endpoint only supports `gpt-image-1` and `dall-e-2`. + This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, + and `gpt-image-1-mini`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. @@ -374,7 +376,8 @@ def edit( """Creates an edited or extended image given one or more source images and a prompt. - This endpoint only supports `gpt-image-1` and `dall-e-2`. + This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, + and `gpt-image-1-mini`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. @@ -1039,7 +1042,8 @@ async def edit( """Creates an edited or extended image given one or more source images and a prompt. - This endpoint only supports `gpt-image-1` and `dall-e-2`. + This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, + and `gpt-image-1-mini`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. @@ -1153,7 +1157,8 @@ async def edit( """Creates an edited or extended image given one or more source images and a prompt. - This endpoint only supports `gpt-image-1` and `dall-e-2`. + This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, + and `gpt-image-1-mini`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. @@ -1267,7 +1272,8 @@ async def edit( """Creates an edited or extended image given one or more source images and a prompt. - This endpoint only supports `gpt-image-1` and `dall-e-2`. + This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, + and `gpt-image-1-mini`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py index 0f47955fe4..8664164655 100644 --- a/src/openai/resources/responses/input_tokens.py +++ b/src/openai/resources/responses/input_tokens.py @@ -102,9 +102,7 @@ def count( - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. + tool_choice: Controls which tool the model should use, if any. tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. @@ -227,9 +225,7 @@ async def count( - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) - tool_choice: How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. + tool_choice: Controls which tool the model should use, if any. tools: An array of tools the model may call while generating a response. You can specify which tool to use by setting the `tool_choice` parameter. diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 37180995c8..417df5b218 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -25,9 +25,9 @@ class SpeechCreateParams(TypedDict, total=False): ] """The voice to use when generating the audio. - Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, - `nova`, `sage`, `shimmer`, and `verse`. Previews of the voices are available in - the + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, + `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. + Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). """ diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index cac3c8b9d4..1a73bb0c7e 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -26,6 +26,6 @@ class ChatCompletionAudioParam(TypedDict, total=False): ] """The voice the model uses to respond. - Supported voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, - `onyx`, `sage`, and `shimmer`. + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, + `fable`, `nova`, `onyx`, `sage`, `shimmer`, `marin`, and `cedar`. """ diff --git a/src/openai/types/realtime/realtime_audio_config_output.py b/src/openai/types/realtime/realtime_audio_config_output.py index a8af237c1d..2922405f63 100644 --- a/src/openai/types/realtime/realtime_audio_config_output.py +++ b/src/openai/types/realtime/realtime_audio_config_output.py @@ -29,8 +29,8 @@ class RealtimeAudioConfigOutput(BaseModel): ] = None """The voice the model uses to respond. - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the + session once the model has responded with audio at least once. We recommend `marin` and `cedar` for best quality. """ diff --git a/src/openai/types/realtime/realtime_audio_config_output_param.py b/src/openai/types/realtime/realtime_audio_config_output_param.py index 8e887d3464..d04fd3a303 100644 --- a/src/openai/types/realtime/realtime_audio_config_output_param.py +++ b/src/openai/types/realtime/realtime_audio_config_output_param.py @@ -28,8 +28,8 @@ class RealtimeAudioConfigOutputParam(TypedDict, total=False): voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] """The voice the model uses to respond. - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the + session once the model has responded with audio at least once. We recommend `marin` and `cedar` for best quality. """ diff --git a/src/openai/types/realtime/realtime_response_create_audio_output.py b/src/openai/types/realtime/realtime_response_create_audio_output.py index b8f4d284d5..db02511ab1 100644 --- a/src/openai/types/realtime/realtime_response_create_audio_output.py +++ b/src/openai/types/realtime/realtime_response_create_audio_output.py @@ -18,10 +18,9 @@ class Output(BaseModel): ] = None """The voice the model uses to respond. - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend - `marin` and `cedar` for best quality. + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the + session once the model has responded with audio at least once. """ diff --git a/src/openai/types/realtime/realtime_response_create_audio_output_param.py b/src/openai/types/realtime/realtime_response_create_audio_output_param.py index 30a4633698..22787ad106 100644 --- a/src/openai/types/realtime/realtime_response_create_audio_output_param.py +++ b/src/openai/types/realtime/realtime_response_create_audio_output_param.py @@ -17,10 +17,9 @@ class Output(TypedDict, total=False): voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] """The voice the model uses to respond. - Voice cannot be changed during the session once the model has responded with - audio at least once. Current voice options are `alloy`, `ash`, `ballad`, - `coral`, `echo`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. We recommend - `marin` and `cedar` for best quality. + Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, + `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the + session once the model has responded with audio at least once. """ diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py index 50cc950e41..97ee4bf6ca 100644 --- a/src/openai/types/responses/input_token_count_params.py +++ b/src/openai/types/responses/input_token_count_params.py @@ -78,11 +78,7 @@ class InputTokenCountParams(TypedDict, total=False): """ tool_choice: Optional[ToolChoice] - """ - How the model should select which tool (or tools) to use when generating a - response. See the `tools` parameter to see how to specify which tools the model - can call. - """ + """Controls which tool the model should use, if any.""" tools: Optional[Iterable[ToolParam]] """An array of tools the model may call while generating a response. diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 00c38c064e..6bac7d65de 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -47,13 +47,13 @@ class IncompleteDetails(BaseModel): class Conversation(BaseModel): - """The conversation that this response belongs to. + """The conversation that this response belonged to. - Input items and output items from this response are automatically added to this conversation. + Input items and output items from this response were automatically added to this conversation. """ id: str - """The unique ID of the conversation.""" + """The unique ID of the conversation that this response was associated with.""" class Response(BaseModel): @@ -165,10 +165,16 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/background). """ + completed_at: Optional[float] = None + """ + Unix timestamp (in seconds) of when this Response was completed. Only present + when the status is `completed`. + """ + conversation: Optional[Conversation] = None - """The conversation that this response belongs to. + """The conversation that this response belonged to. - Input items and output items from this response are automatically added to this + Input items and output items from this response were automatically added to this conversation. """ diff --git a/src/openai/types/responses/response_compaction_item.py b/src/openai/types/responses/response_compaction_item.py index f5f8b97f4e..36e953b127 100644 --- a/src/openai/types/responses/response_compaction_item.py +++ b/src/openai/types/responses/response_compaction_item.py @@ -17,8 +17,10 @@ class ResponseCompactionItem(BaseModel): """The unique ID of the compaction item.""" encrypted_content: str + """The encrypted content that was produced by compaction.""" type: Literal["compaction"] """The type of the item. Always `compaction`.""" created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_compaction_item_param.py b/src/openai/types/responses/response_compaction_item_param.py index 5dcc921d67..5ef134b074 100644 --- a/src/openai/types/responses/response_compaction_item_param.py +++ b/src/openai/types/responses/response_compaction_item_param.py @@ -14,6 +14,7 @@ class ResponseCompactionItemParam(BaseModel): """ encrypted_content: str + """The encrypted content of the compaction summary.""" type: Literal["compaction"] """The type of the item. Always `compaction`.""" diff --git a/src/openai/types/responses/response_compaction_item_param_param.py b/src/openai/types/responses/response_compaction_item_param_param.py index b9b5ab031c..b4d72c2e5f 100644 --- a/src/openai/types/responses/response_compaction_item_param_param.py +++ b/src/openai/types/responses/response_compaction_item_param_param.py @@ -14,6 +14,7 @@ class ResponseCompactionItemParamParam(TypedDict, total=False): """ encrypted_content: Required[str] + """The encrypted content of the compaction summary.""" type: Required[Literal["compaction"]] """The type of the item. Always `compaction`.""" diff --git a/src/openai/types/responses/response_function_shell_tool_call_output.py b/src/openai/types/responses/response_function_shell_tool_call_output.py index 7885ee2f83..5523d57ba7 100644 --- a/src/openai/types/responses/response_function_shell_tool_call_output.py +++ b/src/openai/types/responses/response_function_shell_tool_call_output.py @@ -36,7 +36,7 @@ class OutputOutcomeExit(BaseModel): class Output(BaseModel): - """The content of a shell call output.""" + """The content of a shell tool call output that was emitted.""" outcome: OutputOutcome """ @@ -45,14 +45,17 @@ class Output(BaseModel): """ stderr: str + """The standard error output that was captured.""" stdout: str + """The standard output that was captured.""" created_by: Optional[str] = None + """The identifier of the actor that created the item.""" class ResponseFunctionShellToolCallOutput(BaseModel): - """The output of a shell tool call.""" + """The output of a shell tool call that was emitted.""" id: str """The unique ID of the shell call output. @@ -76,3 +79,4 @@ class ResponseFunctionShellToolCallOutput(BaseModel): """The type of the shell call output. Always `shell_call_output`.""" created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index 1450fba4d1..0cb7e0b0d1 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -23,11 +23,14 @@ class ActionSearch(BaseModel): """Action type "search" - Performs a web search query.""" query: str - """The search query.""" + """[DEPRECATED] The search query.""" type: Literal["search"] """The action type.""" + queries: Optional[List[str]] = None + """The search queries.""" + sources: Optional[List[ActionSearchSource]] = None """The sources used in the search.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 8d0b60334d..7db3e3c833 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -5,6 +5,8 @@ from typing import Union, Iterable from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr + __all__ = [ "ResponseFunctionWebSearchParam", "Action", @@ -29,11 +31,14 @@ class ActionSearch(TypedDict, total=False): """Action type "search" - Performs a web search query.""" query: Required[str] - """The search query.""" + """[DEPRECATED] The search query.""" type: Required[Literal["search"]] """The action type.""" + queries: SequenceNotStr[str] + """The search queries.""" + sources: Iterable[ActionSearchSource] """The sources used in the search.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 20f50e4478..019962a0ba 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -187,6 +187,7 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): """An optional list of uploaded files to make available to your code.""" memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the code interpreter container.""" CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 69c6162153..37d3dde024 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -187,6 +187,7 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): """An optional list of uploaded files to make available to your code.""" memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the code interpreter container.""" CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] diff --git a/src/openai/types/video_create_error.py b/src/openai/types/video_create_error.py index ae328b78ea..7f520220c8 100644 --- a/src/openai/types/video_create_error.py +++ b/src/openai/types/video_create_error.py @@ -6,6 +6,10 @@ class VideoCreateError(BaseModel): + """An error that occurred while generating the response.""" + code: str + """A machine-readable error code that was returned.""" message: str + """A human-readable description of the error that was returned.""" From 722d3fffb82e9150a16da01e432b70d126ca5254 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 21:54:50 +0000 Subject: [PATCH 200/408] release: 2.15.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5868289349..cff01f26f0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.14.0" + ".": "2.15.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ca71bc108e..4630e50610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.15.0 (2026-01-09) + +Full Changelog: [v2.14.0...v2.15.0](https://github.com/openai/openai-python/compare/v2.14.0...v2.15.0) + +### Features + +* **api:** add new Response completed_at prop ([f077752](https://github.com/openai/openai-python/commit/f077752f4a8364a74f784f8fb1cbe31277e1762b)) + + +### Chores + +* **internal:** codegen related update ([e7daba6](https://github.com/openai/openai-python/commit/e7daba6662a3c30f73d991e96cb19d2b54d772e0)) + ## 2.14.0 (2025-12-19) Full Changelog: [v2.13.0...v2.14.0](https://github.com/openai/openai-python/compare/v2.13.0...v2.14.0) diff --git a/pyproject.toml b/pyproject.toml index a720293abb..2d835b05af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.14.0" +version = "2.15.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5fe6b5b08c..6f0ccf7d72 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.14.0" # x-release-please-version +__version__ = "2.15.0" # x-release-please-version From a532f6ef5935f569f0712992d482b3af87fd9e0c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:37:21 +0000 Subject: [PATCH 201/408] feat(client): add support for binary request streaming --- src/openai/_base_client.py | 145 +++++++++++++++++++++++++--- src/openai/_models.py | 17 +++- src/openai/_types.py | 9 ++ tests/test_client.py | 187 ++++++++++++++++++++++++++++++++++++- 4 files changed, 344 insertions(+), 14 deletions(-) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 9e536410d6..d34208abef 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -9,6 +9,7 @@ import inspect import logging import platform +import warnings import email.utils from types import TracebackType from random import random @@ -51,9 +52,11 @@ ResponseT, AnyMapping, PostParser, + BinaryTypes, RequestFiles, HttpxSendArgs, RequestOptions, + AsyncBinaryTypes, HttpxRequestFiles, ModelBuilderProtocol, not_given, @@ -479,8 +482,19 @@ def _build_request( retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): - log.debug("Request options: %s", model_dump(options, exclude_unset=True)) - + log.debug( + "Request options: %s", + model_dump( + options, + exclude_unset=True, + # Pydantic v1 can't dump every type we support in content, so we exclude it for now. + exclude={ + "content", + } + if PYDANTIC_V1 + else {}, + ), + ) kwargs: dict[str, Any] = {} json_data = options.json_data @@ -534,7 +548,13 @@ def _build_request( is_body_allowed = options.method.lower() != "get" if is_body_allowed: - if isinstance(json_data, bytes): + if options.content is not None and json_data is not None: + raise TypeError("Passing both `content` and `json_data` is not supported") + if options.content is not None and files is not None: + raise TypeError("Passing both `content` and `files` is not supported") + if options.content is not None: + kwargs["content"] = options.content + elif isinstance(json_data, bytes): kwargs["content"] = json_data else: kwargs["json"] = json_data if is_given(json_data) else None @@ -1211,6 +1231,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[False] = False, @@ -1223,6 +1244,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[True], @@ -1236,6 +1258,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool, @@ -1248,13 +1271,25 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) @@ -1264,11 +1299,23 @@ def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1278,11 +1325,23 @@ def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1292,9 +1351,19 @@ def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return self.request(cast_to, opts) def get_api_list( @@ -1749,6 +1818,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[False] = False, @@ -1761,6 +1831,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[True], @@ -1774,6 +1845,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool, @@ -1786,13 +1858,25 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) @@ -1802,11 +1886,28 @@ async def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="patch", + url=path, + json_data=body, + content=content, + files=await async_to_httpx_files(files), + **options, ) return await self.request(cast_to, opts) @@ -1816,11 +1917,23 @@ async def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) @@ -1830,9 +1943,19 @@ async def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return await self.request(cast_to, opts) def get_api_list( diff --git a/src/openai/_models.py b/src/openai/_models.py index fac59c2cb8..57b5b448be 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -3,7 +3,20 @@ import os import inspect import weakref -from typing import TYPE_CHECKING, Any, Type, Tuple, Union, Generic, TypeVar, Callable, Optional, cast +from typing import ( + IO, + TYPE_CHECKING, + Any, + Type, Tuple, + Union, + Generic, + TypeVar, + Callable, + Iterable, + Optional, + AsyncIterable, + cast, +) from datetime import date, datetime from typing_extensions import ( List, @@ -827,6 +840,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] json_data: Body extra_json: AnyMapping follow_redirects: bool @@ -845,6 +859,7 @@ class FinalRequestOptions(pydantic.BaseModel): post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() follow_redirects: Union[bool, None] = None + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. json_data: Union[Body, None] = None diff --git a/src/openai/_types.py b/src/openai/_types.py index d7e2eaac5f..42f9df2373 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -13,9 +13,11 @@ Mapping, TypeVar, Callable, + Iterable, Iterator, Optional, Sequence, + AsyncIterable, ) from typing_extensions import ( Set, @@ -57,6 +59,13 @@ else: Base64FileInput = Union[IO[bytes], PathLike] FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. + + +# Used for sending raw binary data / streaming data in request bodies +# e.g. for file uploads without multipart encoding +BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] +AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] + FileTypes = Union[ # file (or bytes) FileContent, diff --git a/tests/test_client.py b/tests/test_client.py index e8d62f17f7..bd243f68dc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -8,10 +8,11 @@ import json import asyncio import inspect +import dataclasses import tracemalloc -from typing import Any, Union, Protocol, cast +from typing import Any, Union, Protocol, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast from unittest import mock -from typing_extensions import Literal +from typing_extensions import Literal, AsyncIterator, override import httpx import pytest @@ -37,6 +38,7 @@ from .utils import update_env +T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" @@ -55,6 +57,57 @@ def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: return 0.1 +def mirror_request_content(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, content=request.content) + + +# note: we can't use the httpx.MockTransport class as it consumes the request +# body itself, which means we can't test that the body is read lazily +class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport): + def __init__( + self, + handler: Callable[[httpx.Request], httpx.Response] + | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]], + ) -> None: + self.handler = handler + + @override + def handle_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function" + assert inspect.isfunction(self.handler), "handler must be a function" + return self.handler(request) + + @override + async def handle_async_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function" + return await self.handler(request) + + +@dataclasses.dataclass +class Counter: + value: int = 0 + + +def _make_sync_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> Iterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + +async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> AsyncIterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int: transport = client._client._transport assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) @@ -507,6 +560,70 @@ def test_multipart_repeating_array(self, client: OpenAI) -> None: b"", ] + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload(self, respx_mock: MockRouter, client: OpenAI) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + def test_binary_content_upload_with_iterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_sync_iterator([file_content], counter=counter) + + def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=request.read()) + + with OpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), + ) as client: + response = client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: OpenAI) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + @pytest.mark.respx(base_url=base_url) def test_basic_union_response(self, respx_mock: MockRouter, client: OpenAI) -> None: class Model1(BaseModel): @@ -1467,6 +1584,72 @@ def test_multipart_repeating_array(self, async_client: AsyncOpenAI) -> None: b"", ] + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = await async_client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + async def test_binary_content_upload_with_asynciterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_async_iterator([file_content], counter=counter) + + async def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=await request.aread()) + + async with AsyncOpenAI( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), + ) as client: + response = await client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload_with_body_is_deprecated( + self, respx_mock: MockRouter, async_client: AsyncOpenAI + ) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = await async_client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + @pytest.mark.respx(base_url=base_url) async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncOpenAI) -> None: class Model1(BaseModel): From 612ad2b5a07c73614e56926c80f486d838fb27f5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:34:06 +0000 Subject: [PATCH 202/408] chore(internal): update `actions/checkout` version --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/create-releases.yml | 2 +- .github/workflows/detect-breaking-changes.yml | 2 +- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c617a6f19..0dc13082f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | @@ -44,7 +44,7 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | @@ -81,7 +81,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | @@ -104,7 +104,7 @@ jobs: if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index b3e1c679d4..f0ef434d02 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -14,7 +14,7 @@ jobs: environment: publish steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: stainless-api/trigger-release-please@v1 id: release diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index 6626d1c376..5f4ecdd97f 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -15,7 +15,7 @@ jobs: run: | echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: # Ensure we can check out the pull request base in the script below. fetch-depth: ${{ env.FETCH_DEPTH }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 32bd6929e2..b9c959ecf3 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -11,7 +11,7 @@ jobs: environment: publish steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index e078964a6f..be7db8df12 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -13,7 +13,7 @@ jobs: if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Check release environment run: | From ef09a0abdd6d169a7fece3c12d57908640cb6a52 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 16:36:17 +0000 Subject: [PATCH 203/408] feat(api): api update --- .stats.yml | 4 +-- src/openai/resources/files.py | 26 +++++++++++++------ src/openai/types/file_create_params.py | 14 +++++----- .../conversation_item_create_event.py | 12 ++++++--- .../conversation_item_create_event_param.py | 12 ++++++--- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/.stats.yml b/.stats.yml index fe01e150f9..7234d96b78 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9442fa9212dd61aac2bb0edd19744bee381e75888712f9098bc6ebb92c52b557.yml -openapi_spec_hash: f87823d164b7a8f72a42eba04e482a99 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2dede2c933d4c80020715c5e1a21c86b353de336e4dd2c6119125e3eaca6f904.yml +openapi_spec_hash: 52ed6a83d460d3b2bf78e54bac8c503d config_hash: ad7136f7366fddec432ec378939e58a7 diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index cc117e7f15..202315be6c 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -91,10 +91,15 @@ def create( Args: file: The File object (not file name) to be uploaded. - purpose: The intended purpose of the uploaded file. One of: - `assistants`: Used in the - Assistants API - `batch`: Used in the Batch API - `fine-tune`: Used for - fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: - Flexible file type for any purpose - `evals`: Used for eval data sets + purpose: + The intended purpose of the uploaded file. One of: + + - `assistants`: Used in the Assistants API + - `batch`: Used in the Batch API + - `fine-tune`: Used for fine-tuning + - `vision`: Images used for vision fine-tuning + - `user_data`: Flexible file type for any purpose + - `evals`: Used for eval data sets expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted. @@ -407,10 +412,15 @@ async def create( Args: file: The File object (not file name) to be uploaded. - purpose: The intended purpose of the uploaded file. One of: - `assistants`: Used in the - Assistants API - `batch`: Used in the Batch API - `fine-tune`: Used for - fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: - Flexible file type for any purpose - `evals`: Used for eval data sets + purpose: + The intended purpose of the uploaded file. One of: + + - `assistants`: Used in the Assistants API + - `batch`: Used in the Batch API + - `fine-tune`: Used for fine-tuning + - `vision`: Images used for vision fine-tuning + - `user_data`: Flexible file type for any purpose + - `evals`: Used for eval data sets expires_after: The expiration policy for a file. By default, files with `purpose=batch` expire after 30 days and all other files are persisted until they are manually deleted. diff --git a/src/openai/types/file_create_params.py b/src/openai/types/file_create_params.py index f4367f7a7d..5e2afc0655 100644 --- a/src/openai/types/file_create_params.py +++ b/src/openai/types/file_create_params.py @@ -15,12 +15,14 @@ class FileCreateParams(TypedDict, total=False): """The File object (not file name) to be uploaded.""" purpose: Required[FilePurpose] - """The intended purpose of the uploaded file. - - One of: - `assistants`: Used in the Assistants API - `batch`: Used in the Batch - API - `fine-tune`: Used for fine-tuning - `vision`: Images used for vision - fine-tuning - `user_data`: Flexible file type for any purpose - `evals`: Used - for eval data sets + """The intended purpose of the uploaded file. One of: + + - `assistants`: Used in the Assistants API + - `batch`: Used in the Batch API + - `fine-tune`: Used for fine-tuning + - `vision`: Images used for vision fine-tuning + - `user_data`: Flexible file type for any purpose + - `evals`: Used for eval data sets """ expires_after: ExpiresAfter diff --git a/src/openai/types/realtime/conversation_item_create_event.py b/src/openai/types/realtime/conversation_item_create_event.py index bf2d129744..fd0fc00fa2 100644 --- a/src/openai/types/realtime/conversation_item_create_event.py +++ b/src/openai/types/realtime/conversation_item_create_event.py @@ -32,8 +32,12 @@ class ConversationItemCreateEvent(BaseModel): previous_item_id: Optional[str] = None """The ID of the preceding item after which the new item will be inserted. - If not set, the new item will be appended to the end of the conversation. If set - to `root`, the new item will be added to the beginning of the conversation. If - set to an existing ID, it allows an item to be inserted mid-conversation. If the - ID cannot be found, an error will be returned and the item will not be added. + If not set, the new item will be appended to the end of the conversation. + + If set to `root`, the new item will be added to the beginning of the + conversation. + + If set to an existing ID, it allows an item to be inserted mid-conversation. If + the ID cannot be found, an error will be returned and the item will not be + added. """ diff --git a/src/openai/types/realtime/conversation_item_create_event_param.py b/src/openai/types/realtime/conversation_item_create_event_param.py index be7f0ff011..e991e37c3b 100644 --- a/src/openai/types/realtime/conversation_item_create_event_param.py +++ b/src/openai/types/realtime/conversation_item_create_event_param.py @@ -32,8 +32,12 @@ class ConversationItemCreateEventParam(TypedDict, total=False): previous_item_id: str """The ID of the preceding item after which the new item will be inserted. - If not set, the new item will be appended to the end of the conversation. If set - to `root`, the new item will be added to the beginning of the conversation. If - set to an existing ID, it allows an item to be inserted mid-conversation. If the - ID cannot be found, an error will be returned and the item will not be added. + If not set, the new item will be appended to the end of the conversation. + + If set to `root`, the new item will be added to the beginning of the + conversation. + + If set to an existing ID, it allows an item to be inserted mid-conversation. If + the ID cannot be found, an error will be returned and the item will not be + added. """ From 3d93d66d52d709878b9b557d0c8518f291939b0a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 05:15:30 +0000 Subject: [PATCH 204/408] codegen metadata --- .stats.yml | 4 ++-- src/openai/_models.py | 3 ++- src/openai/lib/_realtime.py | 4 ++-- tests/test_client.py | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7234d96b78..61563b1404 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2dede2c933d4c80020715c5e1a21c86b353de336e4dd2c6119125e3eaca6f904.yml -openapi_spec_hash: 52ed6a83d460d3b2bf78e54bac8c503d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-51075c225b913785fbb988c5d94e2da629241bf0c414b67b6301ddfffb685a83.yml +openapi_spec_hash: 43be3e1dc6823299a6db37d999aa6f11 config_hash: ad7136f7366fddec432ec378939e58a7 diff --git a/src/openai/_models.py b/src/openai/_models.py index 57b5b448be..5cca20c6f9 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -7,7 +7,8 @@ IO, TYPE_CHECKING, Any, - Type, Tuple, + Type, + Tuple, Union, Generic, TypeVar, diff --git a/src/openai/lib/_realtime.py b/src/openai/lib/_realtime.py index 999d1e4463..3771b52986 100644 --- a/src/openai/lib/_realtime.py +++ b/src/openai/lib/_realtime.py @@ -34,7 +34,7 @@ def create( extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})} return self._post( "/realtime/calls", - body=sdp.encode("utf-8"), + content=sdp.encode("utf-8"), options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, timeout=timeout), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -71,7 +71,7 @@ async def create( extra_headers = {"Accept": "application/sdp", "Content-Type": "application/sdp", **(extra_headers or {})} return await self._post( "/realtime/calls", - body=sdp.encode("utf-8"), + content=sdp.encode("utf-8"), options=make_request_options(extra_headers=extra_headers, extra_query=extra_query, timeout=timeout), cast_to=_legacy_response.HttpxBinaryResponseContent, ) diff --git a/tests/test_client.py b/tests/test_client.py index bd243f68dc..396f6dea99 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,7 +10,7 @@ import inspect import dataclasses import tracemalloc -from typing import Any, Union, Protocol, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast +from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Protocol, Coroutine, cast from unittest import mock from typing_extensions import Literal, AsyncIterator, override From 84e0c1d0d05cd58e28245bff3a1746711dffcb2b Mon Sep 17 00:00:00 2001 From: Charlie Guo Date: Thu, 22 Jan 2026 15:07:34 -0800 Subject: [PATCH 205/408] Update README models to gpt-5.2 --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index b8050a4cd6..7e4f0ae657 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ client = OpenAI( ) response = client.responses.create( - model="gpt-4o", + model="gpt-5.2", instructions="You are a coding assistant that talks like a pirate.", input="How do I check if a Python object is an instance of a class?", ) @@ -52,7 +52,7 @@ from openai import OpenAI client = OpenAI() completion = client.chat.completions.create( - model="gpt-4o", + model="gpt-5.2", messages=[ {"role": "developer", "content": "Talk like a pirate."}, { @@ -80,7 +80,7 @@ prompt = "What is in this image?" img_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg" response = client.responses.create( - model="gpt-4o-mini", + model="gpt-5.2", input=[ { "role": "user", @@ -106,7 +106,7 @@ with open("path/to/image.png", "rb") as image_file: b64_image = base64.b64encode(image_file.read()).decode("utf-8") response = client.responses.create( - model="gpt-4o-mini", + model="gpt-5.2", input=[ { "role": "user", @@ -136,7 +136,7 @@ client = AsyncOpenAI( async def main() -> None: response = await client.responses.create( - model="gpt-4o", input="Explain disestablishmentarianism to a smart five year old." + model="gpt-5.2", input="Explain disestablishmentarianism to a smart five year old." ) print(response.output_text) @@ -178,7 +178,7 @@ async def main() -> None: "content": "Say this is a test", } ], - model="gpt-4o", + model="gpt-5.2", ) @@ -195,7 +195,7 @@ from openai import OpenAI client = OpenAI() stream = client.responses.create( - model="gpt-4o", + model="gpt-5.2", input="Write a one-sentence bedtime story about a unicorn.", stream=True, ) @@ -215,7 +215,7 @@ client = AsyncOpenAI() async def main(): stream = await client.responses.create( - model="gpt-4o", + model="gpt-5.2", input="Write a one-sentence bedtime story about a unicorn.", stream=True, ) @@ -386,7 +386,7 @@ response = client.chat.responses.create( "content": "How much ?", } ], - model="gpt-4o", + model="gpt-5.2", response_format={"type": "json_object"}, ) ``` @@ -541,7 +541,7 @@ All object responses in the SDK provide a `_request_id` property which is added ```python response = await client.responses.create( - model="gpt-4o-mini", + model="gpt-5.2", input="Say 'this is a test'.", ) print(response._request_id) # req_123 @@ -559,7 +559,7 @@ import openai try: completion = await client.chat.completions.create( - messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-4" + messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-5.2" ) except openai.APIStatusError as exc: print(exc.request_id) # req_123 @@ -591,7 +591,7 @@ client.with_options(max_retries=5).chat.completions.create( "content": "How can I get the name of the current day in JavaScript?", } ], - model="gpt-4o", + model="gpt-5.2", ) ``` @@ -622,7 +622,7 @@ client.with_options(timeout=5.0).chat.completions.create( "content": "How can I list all files in a directory using Python?", } ], - model="gpt-4o", + model="gpt-5.2", ) ``` @@ -669,7 +669,7 @@ response = client.chat.completions.with_raw_response.create( "role": "user", "content": "Say this is a test", }], - model="gpt-4o", + model="gpt-5.2", ) print(response.headers.get('X-My-Header')) @@ -702,7 +702,7 @@ with client.chat.completions.with_streaming_response.create( "content": "Say this is a test", } ], - model="gpt-4o", + model="gpt-5.2", ) as response: print(response.headers.get("X-My-Header")) From f424d7b336870308f29170633a779b5d4566b421 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 18:18:03 +0000 Subject: [PATCH 206/408] chore(ci): upgrade `actions/github-script` --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dc13082f6..d087636a64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: - name: Get GitHub OIDC Token if: github.repository == 'stainless-sdks/openai-python' id: github-oidc - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); From d499f4f8050fdd2eb69339e2881977088f622a0b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:31:03 +0000 Subject: [PATCH 207/408] fix(api): mark assistants as deprecated --- .stats.yml | 4 +- src/openai/resources/beta/assistants.py | 131 +++-- src/openai/resources/files.py | 8 +- src/openai/resources/videos.py | 10 +- src/openai/types/video_create_params.py | 5 +- tests/api_resources/beta/test_assistants.py | 532 +++++++++++--------- tests/api_resources/test_videos.py | 2 + 7 files changed, 408 insertions(+), 284 deletions(-) diff --git a/.stats.yml b/.stats.yml index 61563b1404..4f516a48fa 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-51075c225b913785fbb988c5d94e2da629241bf0c414b67b6301ddfffb685a83.yml -openapi_spec_hash: 43be3e1dc6823299a6db37d999aa6f11 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a47fcdd0fd85e2910e56b34ab3239edbb50957af8dca11db4184d3ba2cae9ad8.yml +openapi_spec_hash: ff61f44f41561b462da4a930c4eb84df config_hash: ad7136f7366fddec432ec378939e58a7 diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index ab0947abf4..8c69700059 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -2,6 +2,7 @@ from __future__ import annotations +import typing_extensions from typing import Union, Iterable, Optional from typing_extensions import Literal @@ -51,6 +52,7 @@ def with_streaming_response(self) -> AssistantsWithStreamingResponse: """ return AssistantsWithStreamingResponse(self) + @typing_extensions.deprecated("deprecated") def create( self, *, @@ -183,6 +185,7 @@ def create( cast_to=Assistant, ) + @typing_extensions.deprecated("deprecated") def retrieve( self, assistant_id: str, @@ -217,6 +220,7 @@ def retrieve( cast_to=Assistant, ) + @typing_extensions.deprecated("deprecated") def update( self, assistant_id: str, @@ -400,6 +404,7 @@ def update( cast_to=Assistant, ) + @typing_extensions.deprecated("deprecated") def list( self, *, @@ -465,6 +470,7 @@ def list( model=Assistant, ) + @typing_extensions.deprecated("deprecated") def delete( self, assistant_id: str, @@ -520,6 +526,7 @@ def with_streaming_response(self) -> AsyncAssistantsWithStreamingResponse: """ return AsyncAssistantsWithStreamingResponse(self) + @typing_extensions.deprecated("deprecated") async def create( self, *, @@ -652,6 +659,7 @@ async def create( cast_to=Assistant, ) + @typing_extensions.deprecated("deprecated") async def retrieve( self, assistant_id: str, @@ -686,6 +694,7 @@ async def retrieve( cast_to=Assistant, ) + @typing_extensions.deprecated("deprecated") async def update( self, assistant_id: str, @@ -869,6 +878,7 @@ async def update( cast_to=Assistant, ) + @typing_extensions.deprecated("deprecated") def list( self, *, @@ -934,6 +944,7 @@ def list( model=Assistant, ) + @typing_extensions.deprecated("deprecated") async def delete( self, assistant_id: str, @@ -973,20 +984,30 @@ class AssistantsWithRawResponse: def __init__(self, assistants: Assistants) -> None: self._assistants = assistants - self.create = _legacy_response.to_raw_response_wrapper( - assistants.create, + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - assistants.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) ) - self.update = _legacy_response.to_raw_response_wrapper( - assistants.update, + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) ) - self.list = _legacy_response.to_raw_response_wrapper( - assistants.list, + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) ) - self.delete = _legacy_response.to_raw_response_wrapper( - assistants.delete, + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) ) @@ -994,20 +1015,30 @@ class AsyncAssistantsWithRawResponse: def __init__(self, assistants: AsyncAssistants) -> None: self._assistants = assistants - self.create = _legacy_response.async_to_raw_response_wrapper( - assistants.create, + self.create = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - assistants.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) ) - self.update = _legacy_response.async_to_raw_response_wrapper( - assistants.update, + self.update = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) ) - self.list = _legacy_response.async_to_raw_response_wrapper( - assistants.list, + self.list = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - assistants.delete, + self.delete = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) ) @@ -1015,20 +1046,30 @@ class AssistantsWithStreamingResponse: def __init__(self, assistants: Assistants) -> None: self._assistants = assistants - self.create = to_streamed_response_wrapper( - assistants.create, + self.create = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) ) - self.retrieve = to_streamed_response_wrapper( - assistants.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) ) - self.update = to_streamed_response_wrapper( - assistants.update, + self.update = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) ) - self.list = to_streamed_response_wrapper( - assistants.list, + self.list = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) ) - self.delete = to_streamed_response_wrapper( - assistants.delete, + self.delete = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) ) @@ -1036,18 +1077,28 @@ class AsyncAssistantsWithStreamingResponse: def __init__(self, assistants: AsyncAssistants) -> None: self._assistants = assistants - self.create = async_to_streamed_response_wrapper( - assistants.create, + self.create = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.create, # pyright: ignore[reportDeprecated], + ) ) - self.retrieve = async_to_streamed_response_wrapper( - assistants.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.retrieve, # pyright: ignore[reportDeprecated], + ) ) - self.update = async_to_streamed_response_wrapper( - assistants.update, + self.update = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.update, # pyright: ignore[reportDeprecated], + ) ) - self.list = async_to_streamed_response_wrapper( - assistants.list, + self.list = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.list, # pyright: ignore[reportDeprecated], + ) ) - self.delete = async_to_streamed_response_wrapper( - assistants.delete, + self.delete = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + assistants.delete, # pyright: ignore[reportDeprecated], + ) ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 202315be6c..964d6505e7 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -68,8 +68,8 @@ def create( """Upload a file that can be used across various endpoints. Individual files can be - up to 512 MB, and the size of all files uploaded by one organization can be up - to 1 TB. + up to 512 MB, and each project can store up to 2.5 TB of files in total. There + is no organization-wide storage limit. - The Assistants API supports files up to 2 million tokens and of specific file types. See the @@ -389,8 +389,8 @@ async def create( """Upload a file that can be used across various endpoints. Individual files can be - up to 512 MB, and the size of all files uploaded by one organization can be up - to 1 TB. + up to 512 MB, and each project can store up to 2.5 TB of files in total. There + is no organization-wide storage limit. - The Assistants API supports files up to 2 million tokens and of specific file types. See the diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 9f74c942bc..8d038ed6d3 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -16,7 +16,7 @@ video_create_params, video_download_content_params, ) -from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -64,6 +64,7 @@ def create( self, *, prompt: str, + character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -81,6 +82,8 @@ def create( Args: prompt: Text prompt that describes the video to generate. + character_ids: Character IDs to include in the generation. + input_reference: Optional image reference that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults @@ -102,6 +105,7 @@ def create( body = deepcopy_minimal( { "prompt": prompt, + "character_ids": character_ids, "input_reference": input_reference, "model": model, "seconds": seconds, @@ -419,6 +423,7 @@ async def create( self, *, prompt: str, + character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -436,6 +441,8 @@ async def create( Args: prompt: Text prompt that describes the video to generate. + character_ids: Character IDs to include in the generation. + input_reference: Optional image reference that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults @@ -457,6 +464,7 @@ async def create( body = deepcopy_minimal( { "prompt": prompt, + "character_ids": character_ids, "input_reference": input_reference, "model": model, "seconds": seconds, diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py index d787aaeddd..b5f931af4f 100644 --- a/src/openai/types/video_create_params.py +++ b/src/openai/types/video_create_params.py @@ -4,7 +4,7 @@ from typing_extensions import Required, TypedDict -from .._types import FileTypes +from .._types import FileTypes, SequenceNotStr from .video_size import VideoSize from .video_seconds import VideoSeconds from .video_model_param import VideoModelParam @@ -16,6 +16,9 @@ class VideoCreateParams(TypedDict, total=False): prompt: Required[str] """Text prompt that describes the video to generate.""" + character_ids: SequenceNotStr[str] + """Character IDs to include in the generation.""" + input_reference: FileTypes """Optional image reference that guides generation.""" diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 2557735426..3e85b56dcc 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -15,6 +15,8 @@ AssistantDeleted, ) +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -23,45 +25,50 @@ class TestAssistants: @parametrize def test_method_create(self, client: OpenAI) -> None: - assistant = client.beta.assistants.create( - model="gpt-4o", - ) + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.create( + model="gpt-4o", + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: - assistant = client.beta.assistants.create( - model="gpt-4o", - description="description", - instructions="instructions", - metadata={"foo": "string"}, - name="name", - reasoning_effort="none", - response_format="auto", - temperature=1, - tool_resources={ - "code_interpreter": {"file_ids": ["string"]}, - "file_search": { - "vector_store_ids": ["string"], - "vector_stores": [ - { - "chunking_strategy": {"type": "auto"}, - "file_ids": ["string"], - "metadata": {"foo": "string"}, - } - ], + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.create( + model="gpt-4o", + description="description", + instructions="instructions", + metadata={"foo": "string"}, + name="name", + reasoning_effort="none", + response_format="auto", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "chunking_strategy": {"type": "auto"}, + "file_ids": ["string"], + "metadata": {"foo": "string"}, + } + ], + }, }, - }, - tools=[{"type": "code_interpreter"}], - top_p=1, - ) + tools=[{"type": "code_interpreter"}], + top_p=1, + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_raw_response_create(self, client: OpenAI) -> None: - response = client.beta.assistants.with_raw_response.create( - model="gpt-4o", - ) + with pytest.warns(DeprecationWarning): + response = client.beta.assistants.with_raw_response.create( + model="gpt-4o", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -70,29 +77,33 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: - with client.beta.assistants.with_streaming_response.create( - model="gpt-4o", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.beta.assistants.with_streaming_response.create( + model="gpt-4o", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = response.parse() - assert_matches_type(Assistant, assistant, path=["response"]) + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: OpenAI) -> None: - assistant = client.beta.assistants.retrieve( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.retrieve( + "assistant_id", + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.beta.assistants.with_raw_response.retrieve( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + response = client.beta.assistants.with_raw_response.retrieve( + "assistant_id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -101,57 +112,64 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: - with client.beta.assistants.with_streaming_response.retrieve( - "assistant_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.beta.assistants.with_streaming_response.retrieve( + "assistant_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = response.parse() - assert_matches_type(Assistant, assistant, path=["response"]) + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - client.beta.assistants.with_raw_response.retrieve( - "", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.with_raw_response.retrieve( + "", + ) @parametrize def test_method_update(self, client: OpenAI) -> None: - assistant = client.beta.assistants.update( - assistant_id="assistant_id", - ) + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.update( + assistant_id="assistant_id", + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: OpenAI) -> None: - assistant = client.beta.assistants.update( - assistant_id="assistant_id", - description="description", - instructions="instructions", - metadata={"foo": "string"}, - model="string", - name="name", - reasoning_effort="none", - response_format="auto", - temperature=1, - tool_resources={ - "code_interpreter": {"file_ids": ["string"]}, - "file_search": {"vector_store_ids": ["string"]}, - }, - tools=[{"type": "code_interpreter"}], - top_p=1, - ) + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.update( + assistant_id="assistant_id", + description="description", + instructions="instructions", + metadata={"foo": "string"}, + model="string", + name="name", + reasoning_effort="none", + response_format="auto", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, + tools=[{"type": "code_interpreter"}], + top_p=1, + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize def test_raw_response_update(self, client: OpenAI) -> None: - response = client.beta.assistants.with_raw_response.update( - assistant_id="assistant_id", - ) + with pytest.warns(DeprecationWarning): + response = client.beta.assistants.with_raw_response.update( + assistant_id="assistant_id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -160,42 +178,49 @@ def test_raw_response_update(self, client: OpenAI) -> None: @parametrize def test_streaming_response_update(self, client: OpenAI) -> None: - with client.beta.assistants.with_streaming_response.update( - assistant_id="assistant_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.beta.assistants.with_streaming_response.update( + assistant_id="assistant_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = response.parse() - assert_matches_type(Assistant, assistant, path=["response"]) + assistant = response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - client.beta.assistants.with_raw_response.update( - assistant_id="", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.with_raw_response.update( + assistant_id="", + ) @parametrize def test_method_list(self, client: OpenAI) -> None: - assistant = client.beta.assistants.list() + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.list() + assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: - assistant = client.beta.assistants.list( - after="after", - before="before", - limit=0, - order="asc", - ) + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.list( + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: - response = client.beta.assistants.with_raw_response.list() + with pytest.warns(DeprecationWarning): + response = client.beta.assistants.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -204,27 +229,31 @@ def test_raw_response_list(self, client: OpenAI) -> None: @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: - with client.beta.assistants.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.beta.assistants.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = response.parse() - assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) + assistant = response.parse() + assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: OpenAI) -> None: - assistant = client.beta.assistants.delete( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + assistant = client.beta.assistants.delete( + "assistant_id", + ) + assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: - response = client.beta.assistants.with_raw_response.delete( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + response = client.beta.assistants.with_raw_response.delete( + "assistant_id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -233,23 +262,25 @@ def test_raw_response_delete(self, client: OpenAI) -> None: @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: - with client.beta.assistants.with_streaming_response.delete( - "assistant_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + with client.beta.assistants.with_streaming_response.delete( + "assistant_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = response.parse() - assert_matches_type(AssistantDeleted, assistant, path=["response"]) + assistant = response.parse() + assert_matches_type(AssistantDeleted, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: OpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - client.beta.assistants.with_raw_response.delete( - "", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + client.beta.assistants.with_raw_response.delete( + "", + ) class TestAsyncAssistants: @@ -259,45 +290,50 @@ class TestAsyncAssistants: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.create( - model="gpt-4o", - ) + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.create( + model="gpt-4o", + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.create( - model="gpt-4o", - description="description", - instructions="instructions", - metadata={"foo": "string"}, - name="name", - reasoning_effort="none", - response_format="auto", - temperature=1, - tool_resources={ - "code_interpreter": {"file_ids": ["string"]}, - "file_search": { - "vector_store_ids": ["string"], - "vector_stores": [ - { - "chunking_strategy": {"type": "auto"}, - "file_ids": ["string"], - "metadata": {"foo": "string"}, - } - ], + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.create( + model="gpt-4o", + description="description", + instructions="instructions", + metadata={"foo": "string"}, + name="name", + reasoning_effort="none", + response_format="auto", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string"]}, + "file_search": { + "vector_store_ids": ["string"], + "vector_stores": [ + { + "chunking_strategy": {"type": "auto"}, + "file_ids": ["string"], + "metadata": {"foo": "string"}, + } + ], + }, }, - }, - tools=[{"type": "code_interpreter"}], - top_p=1, - ) + tools=[{"type": "code_interpreter"}], + top_p=1, + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.with_raw_response.create( - model="gpt-4o", - ) + with pytest.warns(DeprecationWarning): + response = await async_client.beta.assistants.with_raw_response.create( + model="gpt-4o", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -306,29 +342,33 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.with_streaming_response.create( - model="gpt-4o", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + async with async_client.beta.assistants.with_streaming_response.create( + model="gpt-4o", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = await response.parse() - assert_matches_type(Assistant, assistant, path=["response"]) + assistant = await response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.retrieve( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.retrieve( + "assistant_id", + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.with_raw_response.retrieve( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + response = await async_client.beta.assistants.with_raw_response.retrieve( + "assistant_id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -337,57 +377,64 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.with_streaming_response.retrieve( - "assistant_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + async with async_client.beta.assistants.with_streaming_response.retrieve( + "assistant_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = await response.parse() - assert_matches_type(Assistant, assistant, path=["response"]) + assistant = await response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await async_client.beta.assistants.with_raw_response.retrieve( - "", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await async_client.beta.assistants.with_raw_response.retrieve( + "", + ) @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.update( - assistant_id="assistant_id", - ) + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.update( + assistant_id="assistant_id", + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.update( - assistant_id="assistant_id", - description="description", - instructions="instructions", - metadata={"foo": "string"}, - model="string", - name="name", - reasoning_effort="none", - response_format="auto", - temperature=1, - tool_resources={ - "code_interpreter": {"file_ids": ["string"]}, - "file_search": {"vector_store_ids": ["string"]}, - }, - tools=[{"type": "code_interpreter"}], - top_p=1, - ) + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.update( + assistant_id="assistant_id", + description="description", + instructions="instructions", + metadata={"foo": "string"}, + model="string", + name="name", + reasoning_effort="none", + response_format="auto", + temperature=1, + tool_resources={ + "code_interpreter": {"file_ids": ["string"]}, + "file_search": {"vector_store_ids": ["string"]}, + }, + tools=[{"type": "code_interpreter"}], + top_p=1, + ) + assert_matches_type(Assistant, assistant, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.with_raw_response.update( - assistant_id="assistant_id", - ) + with pytest.warns(DeprecationWarning): + response = await async_client.beta.assistants.with_raw_response.update( + assistant_id="assistant_id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -396,42 +443,49 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.with_streaming_response.update( - assistant_id="assistant_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + async with async_client.beta.assistants.with_streaming_response.update( + assistant_id="assistant_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = await response.parse() - assert_matches_type(Assistant, assistant, path=["response"]) + assistant = await response.parse() + assert_matches_type(Assistant, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await async_client.beta.assistants.with_raw_response.update( - assistant_id="", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await async_client.beta.assistants.with_raw_response.update( + assistant_id="", + ) @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.list() + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.list() + assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.list( - after="after", - before="before", - limit=0, - order="asc", - ) + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.list( + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.with_raw_response.list() + with pytest.warns(DeprecationWarning): + response = await async_client.beta.assistants.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -440,27 +494,31 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + async with async_client.beta.assistants.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = await response.parse() - assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) + assistant = await response.parse() + assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: - assistant = await async_client.beta.assistants.delete( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + assistant = await async_client.beta.assistants.delete( + "assistant_id", + ) + assert_matches_type(AssistantDeleted, assistant, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: - response = await async_client.beta.assistants.with_raw_response.delete( - "assistant_id", - ) + with pytest.warns(DeprecationWarning): + response = await async_client.beta.assistants.with_raw_response.delete( + "assistant_id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -469,20 +527,22 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: - async with async_client.beta.assistants.with_streaming_response.delete( - "assistant_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with pytest.warns(DeprecationWarning): + async with async_client.beta.assistants.with_streaming_response.delete( + "assistant_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - assistant = await response.parse() - assert_matches_type(AssistantDeleted, assistant, path=["response"]) + assistant = await response.parse() + assert_matches_type(AssistantDeleted, assistant, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): - await async_client.beta.assistants.with_raw_response.delete( - "", - ) + with pytest.warns(DeprecationWarning): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `assistant_id` but received ''"): + await async_client.beta.assistants.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py index b785e03ca5..2f1bce5f84 100644 --- a/tests/api_resources/test_videos.py +++ b/tests/api_resources/test_videos.py @@ -38,6 +38,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: video = client.videos.create( prompt="x", + character_ids=["char_123"], input_reference=b"raw file contents", model="string", seconds="4", @@ -296,6 +297,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: video = await async_client.videos.create( prompt="x", + character_ids=["char_123"], input_reference=b"raw file contents", model="string", seconds="4", From dc93407352ea88439c58d5bbb028e6f1b703bba4 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Fri, 23 Jan 2026 16:41:22 -0500 Subject: [PATCH 208/408] fix helper --- src/openai/resources/videos.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 8d038ed6d3..e201177baa 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -132,6 +132,7 @@ def create_and_poll( self, *, prompt: str, + character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -148,6 +149,7 @@ def create_and_poll( video = self.create( model=model, prompt=prompt, + character_ids=character_ids, input_reference=input_reference, seconds=seconds, size=size, @@ -491,6 +493,7 @@ async def create_and_poll( self, *, prompt: str, + character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -507,6 +510,7 @@ async def create_and_poll( video = await self.create( model=model, prompt=prompt, + character_ids=character_ids, input_reference=input_reference, seconds=seconds, size=size, From 8be9907031c1cad0433c0e9543693c9b64c29453 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Fri, 23 Jan 2026 22:09:40 -0500 Subject: [PATCH 209/408] fix breaking change detection with deprecations --- .github/workflows/detect-breaking-changes.yml | 4 +--- pyproject.toml | 2 +- scripts/detect-breaking-changes | 2 +- scripts/pyrightconfig.breaking-changes.json | 4 ++++ scripts/run-pyright | 8 ++++++++ 5 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 scripts/pyrightconfig.breaking-changes.json create mode 100755 scripts/run-pyright diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index 5f4ecdd97f..87c02061c2 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -36,9 +36,7 @@ jobs: - name: Detect breaking changes run: | - # Try to check out previous versions of the breaking change detection script. This ensures that - # we still detect breaking changes when entire files and their tests are removed. - git checkout "${{ github.event.pull_request.base.sha }}" -- ./scripts/detect-breaking-changes 2>/dev/null || true + test -f ./scripts/detect-breaking-changes || { echo "Missing scripts/detect-breaking-changes"; exit 1; } ./scripts/detect-breaking-changes ${{ github.event.pull_request.base.sha }} agents_sdk: diff --git a/pyproject.toml b/pyproject.toml index 2d835b05af..8bdc519acf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,7 +101,7 @@ typecheck = { chain = [ "typecheck:pyright", "typecheck:mypy" ]} -"typecheck:pyright" = "pyright" +"typecheck:pyright" = "scripts/run-pyright" "typecheck:verify-types" = "pyright --verifytypes openai --ignoreexternal" "typecheck:mypy" = "mypy ." diff --git a/scripts/detect-breaking-changes b/scripts/detect-breaking-changes index 833872ef3a..25b6aa485f 100755 --- a/scripts/detect-breaking-changes +++ b/scripts/detect-breaking-changes @@ -21,4 +21,4 @@ done # Instead of running the tests, use the linter to check if an # older test is no longer compatible with the latest SDK. -./scripts/lint +PYRIGHT_PROJECT=scripts/pyrightconfig.breaking-changes.json ./scripts/lint diff --git a/scripts/pyrightconfig.breaking-changes.json b/scripts/pyrightconfig.breaking-changes.json new file mode 100644 index 0000000000..87bfd3bbc0 --- /dev/null +++ b/scripts/pyrightconfig.breaking-changes.json @@ -0,0 +1,4 @@ +{ + "extends": "../pyproject.toml", + "reportDeprecated": false +} diff --git a/scripts/run-pyright b/scripts/run-pyright new file mode 100755 index 0000000000..1c71ba0587 --- /dev/null +++ b/scripts/run-pyright @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cd "$(dirname "$0")/.." + +CONFIG=${PYRIGHT_PROJECT:-pyproject.toml} +exec pyright -p "$CONFIG" "$@" From d167d1456cd27e0448ed2cb91b8bdc576bec5b23 Mon Sep 17 00:00:00 2001 From: Andreas Lachenmann <53483608+andreasjl@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:57:08 +0100 Subject: [PATCH 210/408] docs(examples): update Azure Realtime sample to use v1 API (#2829) --- examples/realtime/azure_realtime.py | 33 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/examples/realtime/azure_realtime.py b/examples/realtime/azure_realtime.py index 3cf64b8be9..177c24fdfa 100644 --- a/examples/realtime/azure_realtime.py +++ b/examples/realtime/azure_realtime.py @@ -3,7 +3,7 @@ from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider -from openai import AsyncAzureOpenAI +from openai import AsyncOpenAI # Azure OpenAI Realtime Docs @@ -21,18 +21,37 @@ async def main() -> None: """ credential = DefaultAzureCredential() - client = AsyncAzureOpenAI( - azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"], - azure_ad_token_provider=get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default"), - api_version="2024-10-01-preview", + token_provider = get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default") + token = await token_provider() + + # The endpoint of your Azure OpenAI resource is required. You can set it in the AZURE_OPENAI_ENDPOINT + # environment variable. + # You can find it in the Microsoft Foundry portal in the Overview page of your Azure OpenAI resource. + # Example: https://{your-resource}.openai.azure.com + endpoint = os.environ["AZURE_OPENAI_ENDPOINT"] + + # The deployment name of the model you want to use is required. You can set it in the AZURE_OPENAI_DEPLOYMENT_NAME + # environment variable. + # You can find it in the Foundry portal in the "Models + endpoints" page of your Azure OpenAI resource. + # Example: gpt-realtime + deployment_name = os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"] + + base_url = endpoint.replace("https://", "wss://").rstrip("/") + "/openai/v1" + + # The APIs are compatible with the OpenAI client library. + # You can use the OpenAI client library to access the Azure OpenAI APIs. + # Make sure to set the baseURL and apiKey to use the Azure OpenAI endpoint and token. + client = AsyncOpenAI( + websocket_base_url=base_url, + api_key=token ) async with client.realtime.connect( - model="gpt-realtime", # deployment name for your model + model=deployment_name, ) as connection: await connection.session.update( session={ "output_modalities": ["text"], - "model": "gpt-realtime", + "model": deployment_name, "type": "realtime", } ) From eaab2f5c55cbc72d79439f3c114c49a0b9625ffa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 27 Jan 2026 23:21:10 +0000 Subject: [PATCH 211/408] feat(api): api updates --- .stats.yml | 4 ++-- src/openai/resources/videos.py | 10 +--------- .../response_function_shell_tool_call_output.py | 6 ++++++ src/openai/types/video_create_params.py | 5 +---- tests/api_resources/test_videos.py | 2 -- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4f516a48fa..a43021242b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a47fcdd0fd85e2910e56b34ab3239edbb50957af8dca11db4184d3ba2cae9ad8.yml -openapi_spec_hash: ff61f44f41561b462da4a930c4eb84df +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-506f44f37cccac43899267dd64cc5615e96f6e15f2736aa37e5e4eed2eccc567.yml +openapi_spec_hash: d242c25afd700d928787a46e7901fa45 config_hash: ad7136f7366fddec432ec378939e58a7 diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index e201177baa..e8740d48c2 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -16,7 +16,7 @@ video_create_params, video_download_content_params, ) -from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given +from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -64,7 +64,6 @@ def create( self, *, prompt: str, - character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -82,8 +81,6 @@ def create( Args: prompt: Text prompt that describes the video to generate. - character_ids: Character IDs to include in the generation. - input_reference: Optional image reference that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults @@ -105,7 +102,6 @@ def create( body = deepcopy_minimal( { "prompt": prompt, - "character_ids": character_ids, "input_reference": input_reference, "model": model, "seconds": seconds, @@ -425,7 +421,6 @@ async def create( self, *, prompt: str, - character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -443,8 +438,6 @@ async def create( Args: prompt: Text prompt that describes the video to generate. - character_ids: Character IDs to include in the generation. - input_reference: Optional image reference that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults @@ -466,7 +459,6 @@ async def create( body = deepcopy_minimal( { "prompt": prompt, - "character_ids": character_ids, "input_reference": input_reference, "model": model, "seconds": seconds, diff --git a/src/openai/types/responses/response_function_shell_tool_call_output.py b/src/openai/types/responses/response_function_shell_tool_call_output.py index 5523d57ba7..7196ab47f7 100644 --- a/src/openai/types/responses/response_function_shell_tool_call_output.py +++ b/src/openai/types/responses/response_function_shell_tool_call_output.py @@ -75,6 +75,12 @@ class ResponseFunctionShellToolCallOutput(BaseModel): output: List[Output] """An array of shell call output contents""" + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call output. + + One of `in_progress`, `completed`, or `incomplete`. + """ + type: Literal["shell_call_output"] """The type of the shell call output. Always `shell_call_output`.""" diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py index b5f931af4f..d787aaeddd 100644 --- a/src/openai/types/video_create_params.py +++ b/src/openai/types/video_create_params.py @@ -4,7 +4,7 @@ from typing_extensions import Required, TypedDict -from .._types import FileTypes, SequenceNotStr +from .._types import FileTypes from .video_size import VideoSize from .video_seconds import VideoSeconds from .video_model_param import VideoModelParam @@ -16,9 +16,6 @@ class VideoCreateParams(TypedDict, total=False): prompt: Required[str] """Text prompt that describes the video to generate.""" - character_ids: SequenceNotStr[str] - """Character IDs to include in the generation.""" - input_reference: FileTypes """Optional image reference that guides generation.""" diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py index 2f1bce5f84..b785e03ca5 100644 --- a/tests/api_resources/test_videos.py +++ b/tests/api_resources/test_videos.py @@ -38,7 +38,6 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: video = client.videos.create( prompt="x", - character_ids=["char_123"], input_reference=b"raw file contents", model="string", seconds="4", @@ -297,7 +296,6 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: video = await async_client.videos.create( prompt="x", - character_ids=["char_123"], input_reference=b"raw file contents", model="string", seconds="4", From c7f70bd7aeea59e7f4d62813a884a47d11e5b155 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 27 Jan 2026 18:23:20 -0500 Subject: [PATCH 212/408] fix videos --- src/openai/resources/videos.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index e8740d48c2..9f74c942bc 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -128,7 +128,6 @@ def create_and_poll( self, *, prompt: str, - character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -145,7 +144,6 @@ def create_and_poll( video = self.create( model=model, prompt=prompt, - character_ids=character_ids, input_reference=input_reference, seconds=seconds, size=size, @@ -485,7 +483,6 @@ async def create_and_poll( self, *, prompt: str, - character_ids: SequenceNotStr[str] | Omit = omit, input_reference: FileTypes | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, @@ -502,7 +499,6 @@ async def create_and_poll( video = await self.create( model=model, prompt=prompt, - character_ids=character_ids, input_reference=input_reference, seconds=seconds, size=size, From dc68b90655912886bd7a6c7787f96005452ebfc9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 27 Jan 2026 23:24:00 +0000 Subject: [PATCH 213/408] release: 2.16.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 26 ++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cff01f26f0..b258565371 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.15.0" + ".": "2.16.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4630e50610..28902cb2b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## 2.16.0 (2026-01-27) + +Full Changelog: [v2.15.0...v2.16.0](https://github.com/openai/openai-python/compare/v2.15.0...v2.16.0) + +### Features + +* **api:** api update ([b97f9f2](https://github.com/openai/openai-python/commit/b97f9f26b9c46ca4519130e60a8bf12ad8d52bf3)) +* **api:** api updates ([9debcc0](https://github.com/openai/openai-python/commit/9debcc02370f5b76a6a609ded18fbf8dea87b9cb)) +* **client:** add support for binary request streaming ([49561d8](https://github.com/openai/openai-python/commit/49561d88279628bc400d1b09aa98765b67018ef1)) + + +### Bug Fixes + +* **api:** mark assistants as deprecated ([0419cbc](https://github.com/openai/openai-python/commit/0419cbcbf1021131c7492321436ed01ca4337835)) + + +### Chores + +* **ci:** upgrade `actions/github-script` ([5139f13](https://github.com/openai/openai-python/commit/5139f13ef35e64dadc65f2ba2bab736977985769)) +* **internal:** update `actions/checkout` version ([f276714](https://github.com/openai/openai-python/commit/f2767144c11833070c0579063ed33918089b4617)) + + +### Documentation + +* **examples:** update Azure Realtime sample to use v1 API ([#2829](https://github.com/openai/openai-python/issues/2829)) ([3b31981](https://github.com/openai/openai-python/commit/3b319819544d629c5b8c206b8b1f6ec6328c6136)) + ## 2.15.0 (2026-01-09) Full Changelog: [v2.14.0...v2.15.0](https://github.com/openai/openai-python/compare/v2.14.0...v2.15.0) diff --git a/pyproject.toml b/pyproject.toml index 8bdc519acf..bd75d0096d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.15.0" +version = "2.16.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6f0ccf7d72..eb61bdd2c6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.15.0" # x-release-please-version +__version__ = "2.16.0" # x-release-please-version From 27feb0a1b4fc821a47dd6898c5ab8cd2ac3238f4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 22:21:46 +0000 Subject: [PATCH 214/408] feat(api): add shell_call_output status field --- .stats.yml | 6 +++--- src/openai/types/responses/response_input_item.py | 3 +++ src/openai/types/responses/response_input_item_param.py | 3 +++ src/openai/types/responses/response_input_param.py | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index a43021242b..5020dde626 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-506f44f37cccac43899267dd64cc5615e96f6e15f2736aa37e5e4eed2eccc567.yml -openapi_spec_hash: d242c25afd700d928787a46e7901fa45 -config_hash: ad7136f7366fddec432ec378939e58a7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f1f6d4ab31f58e4d3dd84b28802a7cba3b3f042f9d2940e0dae2b133af064968.yml +openapi_spec_hash: 4f5eaf9930f283a039c1a536961591d4 +config_hash: d827d220d7967208cd0207c0518554b7 diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index 23eb2c8950..e1f521f957 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -285,6 +285,9 @@ class ShellCallOutput(BaseModel): output. """ + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call output.""" + class ApplyPatchCallOperationCreateFile(BaseModel): """Instruction for creating a new file via the apply_patch tool.""" diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 2c42b93021..03fe86c42c 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -286,6 +286,9 @@ class ShellCallOutput(TypedDict, total=False): output. """ + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + class ApplyPatchCallOperationCreateFile(TypedDict, total=False): """Instruction for creating a new file via the apply_patch tool.""" diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index c2d12c0ab4..5be65b3e14 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -287,6 +287,9 @@ class ShellCallOutput(TypedDict, total=False): output. """ + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + class ApplyPatchCallOperationCreateFile(TypedDict, total=False): """Instruction for creating a new file via the apply_patch tool.""" From 7da396e2601ea1587c8798a9c60d9d3497146380 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:45:56 +0000 Subject: [PATCH 215/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 5020dde626..8716fd480e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f1f6d4ab31f58e4d3dd84b28802a7cba3b3f042f9d2940e0dae2b133af064968.yml openapi_spec_hash: 4f5eaf9930f283a039c1a536961591d4 -config_hash: d827d220d7967208cd0207c0518554b7 +config_hash: aa92a4ffc7ee4b1d5ac0503d1d0c1fea From 2360dfa7fd26a8f92211702c04752a10fe5fff27 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:28:55 +0000 Subject: [PATCH 216/408] codegen metadata --- .stats.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8716fd480e..3bd66743a1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-f1f6d4ab31f58e4d3dd84b28802a7cba3b3f042f9d2940e0dae2b133af064968.yml -openapi_spec_hash: 4f5eaf9930f283a039c1a536961591d4 -config_hash: aa92a4ffc7ee4b1d5ac0503d1d0c1fea +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7a6ba5212fa9680f9489f4e73c298e1e8f78106b83a465e76290d45fc2fccb70.yml +openapi_spec_hash: 3d3379b7dbf6af484944bc678ec9bf4c +config_hash: a08002d1759a1d0bde3429dccc58d1ef From db4d87193089f60d8a2c2841ded3c7fdcd54a5bb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:56:16 +0000 Subject: [PATCH 217/408] feat(client): add custom JSON encoder for extended type support --- src/openai/_base_client.py | 7 +- src/openai/_compat.py | 6 +- src/openai/_utils/_json.py | 35 ++++++++++ tests/test_utils/test_json.py | 126 ++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 src/openai/_utils/_json.py create mode 100644 tests/test_utils/test_json.py diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index d34208abef..17863bc067 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -86,6 +86,7 @@ APIConnectionError, APIResponseValidationError, ) +from ._utils._json import openapi_dumps from ._legacy_response import LegacyAPIResponse log: logging.Logger = logging.getLogger(__name__) @@ -556,8 +557,10 @@ def _build_request( kwargs["content"] = options.content elif isinstance(json_data, bytes): kwargs["content"] = json_data - else: - kwargs["json"] = json_data if is_given(json_data) else None + elif not files: + # Don't set content when JSON is sent as multipart/form-data, + # since httpx's content param overrides other body arguments + kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None kwargs["files"] = files else: headers.pop("Content-Type", None) diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 73a1f3ea93..020ffeb2ca 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -139,6 +139,7 @@ def model_dump( exclude_defaults: bool = False, warnings: bool = True, mode: Literal["json", "python"] = "python", + by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): return model.model_dump( @@ -148,13 +149,12 @@ def model_dump( exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, + by_alias=by_alias, ) return cast( "dict[str, Any]", model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - exclude=exclude, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) ), ) diff --git a/src/openai/_utils/_json.py b/src/openai/_utils/_json.py new file mode 100644 index 0000000000..60584214af --- /dev/null +++ b/src/openai/_utils/_json.py @@ -0,0 +1,35 @@ +import json +from typing import Any +from datetime import datetime +from typing_extensions import override + +import pydantic + +from .._compat import model_dump + + +def openapi_dumps(obj: Any) -> bytes: + """ + Serialize an object to UTF-8 encoded JSON bytes. + + Extends the standard json.dumps with support for additional types + commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc. + """ + return json.dumps( + obj, + cls=_CustomEncoder, + # Uses the same defaults as httpx's JSON serialization + ensure_ascii=False, + separators=(",", ":"), + allow_nan=False, + ).encode() + + +class _CustomEncoder(json.JSONEncoder): + @override + def default(self, o: Any) -> Any: + if isinstance(o, datetime): + return o.isoformat() + if isinstance(o, pydantic.BaseModel): + return model_dump(o, exclude_unset=True, mode="json", by_alias=True) + return super().default(o) diff --git a/tests/test_utils/test_json.py b/tests/test_utils/test_json.py new file mode 100644 index 0000000000..240c64562f --- /dev/null +++ b/tests/test_utils/test_json.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import datetime +from typing import Union + +import pydantic + +from openai import _compat +from openai._utils._json import openapi_dumps + + +class TestOpenapiDumps: + def test_basic(self) -> None: + data = {"key": "value", "number": 42} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"key":"value","number":42}' + + def test_datetime_serialization(self) -> None: + dt = datetime.datetime(2023, 1, 1, 12, 0, 0) + data = {"datetime": dt} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"datetime":"2023-01-01T12:00:00"}' + + def test_pydantic_model_serialization(self) -> None: + class User(pydantic.BaseModel): + first_name: str + last_name: str + age: int + + model_instance = User(first_name="John", last_name="Kramer", age=83) + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"first_name":"John","last_name":"Kramer","age":83}}' + + def test_pydantic_model_with_default_values(self) -> None: + class User(pydantic.BaseModel): + name: str + role: str = "user" + active: bool = True + score: int = 0 + + model_instance = User(name="Alice") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Alice"}}' + + def test_pydantic_model_with_default_values_overridden(self) -> None: + class User(pydantic.BaseModel): + name: str + role: str = "user" + active: bool = True + + model_instance = User(name="Bob", role="admin", active=False) + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Bob","role":"admin","active":false}}' + + def test_pydantic_model_with_alias(self) -> None: + class User(pydantic.BaseModel): + first_name: str = pydantic.Field(alias="firstName") + last_name: str = pydantic.Field(alias="lastName") + + model_instance = User(firstName="John", lastName="Doe") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"firstName":"John","lastName":"Doe"}}' + + def test_pydantic_model_with_alias_and_default(self) -> None: + class User(pydantic.BaseModel): + user_name: str = pydantic.Field(alias="userName") + user_role: str = pydantic.Field(default="member", alias="userRole") + is_active: bool = pydantic.Field(default=True, alias="isActive") + + model_instance = User(userName="charlie") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"userName":"charlie"}}' + + model_with_overrides = User(userName="diana", userRole="admin", isActive=False) + data = {"model": model_with_overrides} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"userName":"diana","userRole":"admin","isActive":false}}' + + def test_pydantic_model_with_nested_models_and_defaults(self) -> None: + class Address(pydantic.BaseModel): + street: str + city: str = "Unknown" + + class User(pydantic.BaseModel): + name: str + address: Address + verified: bool = False + + if _compat.PYDANTIC_V1: + # to handle forward references in Pydantic v1 + User.update_forward_refs(**locals()) # type: ignore[reportDeprecated] + + address = Address(street="123 Main St") + user = User(name="Diana", address=address) + data = {"user": user} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"user":{"name":"Diana","address":{"street":"123 Main St"}}}' + + address_with_city = Address(street="456 Oak Ave", city="Boston") + user_verified = User(name="Eve", address=address_with_city, verified=True) + data = {"user": user_verified} + json_bytes = openapi_dumps(data) + assert ( + json_bytes == b'{"user":{"name":"Eve","address":{"street":"456 Oak Ave","city":"Boston"},"verified":true}}' + ) + + def test_pydantic_model_with_optional_fields(self) -> None: + class User(pydantic.BaseModel): + name: str + email: Union[str, None] + phone: Union[str, None] + + model_with_none = User(name="Eve", email=None, phone=None) + data = {"model": model_with_none} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Eve","email":null,"phone":null}}' + + model_with_values = User(name="Frank", email="frank@example.com", phone=None) + data = {"model": model_with_values} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Frank","email":"frank@example.com","phone":null}}' From 42cb178759bd2bac2274f4c7afd3c550e6cf9aa2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:43:01 +0000 Subject: [PATCH 218/408] feat(api): image generation actions for responses; ResponseFunctionCallArgumentsDoneEvent.name --- .stats.yml | 6 +++--- .../realtime/response_function_call_arguments_done_event.py | 3 +++ src/openai/types/responses/response_function_web_search.py | 4 ++-- .../types/responses/response_function_web_search_param.py | 6 +++--- src/openai/types/responses/tool.py | 5 ++++- src/openai/types/responses/tool_param.py | 5 ++++- 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3bd66743a1..fcd2a277ae 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7a6ba5212fa9680f9489f4e73c298e1e8f78106b83a465e76290d45fc2fccb70.yml -openapi_spec_hash: 3d3379b7dbf6af484944bc678ec9bf4c -config_hash: a08002d1759a1d0bde3429dccc58d1ef +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b956f0004bb930006f4b8d24734b20e89c7420ca6635dd358b9f0299c8ac8d62.yml +openapi_spec_hash: 91b1b7bf3c1a6b6c9c7507d4cac8fe2a +config_hash: 9501b3367f98ede2729f1bfffb3a803a diff --git a/src/openai/types/realtime/response_function_call_arguments_done_event.py b/src/openai/types/realtime/response_function_call_arguments_done_event.py index 504f91d558..01bae80562 100644 --- a/src/openai/types/realtime/response_function_call_arguments_done_event.py +++ b/src/openai/types/realtime/response_function_call_arguments_done_event.py @@ -25,6 +25,9 @@ class ResponseFunctionCallArgumentsDoneEvent(BaseModel): item_id: str """The ID of the function call item.""" + name: str + """The name of the function that was called.""" + output_index: int """The index of the output item in the response.""" diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index 0cb7e0b0d1..90d422eece 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -41,7 +41,7 @@ class ActionOpenPage(BaseModel): type: Literal["open_page"] """The action type.""" - url: str + url: Optional[str] = None """The URL opened by the model.""" @@ -74,7 +74,7 @@ class ResponseFunctionWebSearch(BaseModel): action: Action """ An object describing the specific action taken in this web search call. Includes - details on how the model used the web (search, open_page, find). + details on how the model used the web (search, open_page, find_in_page). """ status: Literal["in_progress", "searching", "completed", "failed"] diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 7db3e3c833..017e35efbc 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr @@ -49,7 +49,7 @@ class ActionOpenPage(TypedDict, total=False): type: Required[Literal["open_page"]] """The action type.""" - url: Required[str] + url: Optional[str] """The URL opened by the model.""" @@ -82,7 +82,7 @@ class ResponseFunctionWebSearchParam(TypedDict, total=False): action: Required[Action] """ An object describing the specific action taken in this web search call. Includes - details on how the model used the web (search, open_page, find). + details on how the model used the web (search, open_page, find_in_page). """ status: Required[Literal["in_progress", "searching", "completed", "failed"]] diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 019962a0ba..435f046768 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -227,6 +227,9 @@ class ImageGeneration(BaseModel): type: Literal["image_generation"] """The type of the image generation tool. Always `image_generation`.""" + action: Optional[Literal["generate", "edit", "auto"]] = None + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + background: Optional[Literal["transparent", "opaque", "auto"]] = None """Background type for the generated image. @@ -247,7 +250,7 @@ class ImageGeneration(BaseModel): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini"], None] = None + model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"], None] = None """The image generation model to use. Default: `gpt-image-1`.""" moderation: Optional[Literal["auto", "low"]] = None diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 37d3dde024..3afff87892 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -227,6 +227,9 @@ class ImageGeneration(TypedDict, total=False): type: Required[Literal["image_generation"]] """The type of the image generation tool. Always `image_generation`.""" + action: Literal["generate", "edit", "auto"] + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + background: Literal["transparent", "opaque", "auto"] """Background type for the generated image. @@ -247,7 +250,7 @@ class ImageGeneration(TypedDict, total=False): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini"]] + model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"]] """The image generation model to use. Default: `gpt-image-1`.""" moderation: Literal["auto", "low"] From a1fb97bb3580d58a4534a3b4278b5cd4a43ddbc6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 21:36:28 +0000 Subject: [PATCH 219/408] fix(client): update type for `find_in_page` action --- .stats.yml | 4 ++-- .../responses/response_function_web_search.py | 17 ++++++++++++----- .../response_function_web_search_param.py | 10 +++++----- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index fcd2a277ae..d9e272c17f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b956f0004bb930006f4b8d24734b20e89c7420ca6635dd358b9f0299c8ac8d62.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c3a646eb5dcad2b7ff7bd976c0e312b886676a542f6ffcd9a6c8503ae24c58.yml openapi_spec_hash: 91b1b7bf3c1a6b6c9c7507d4cac8fe2a -config_hash: 9501b3367f98ede2729f1bfffb3a803a +config_hash: f8e6baff429cf000b8e4ba1da08dff47 diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index 90d422eece..e0a9caf43b 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -6,7 +6,14 @@ from ..._utils import PropertyInfo from ..._models import BaseModel -__all__ = ["ResponseFunctionWebSearch", "Action", "ActionSearch", "ActionSearchSource", "ActionOpenPage", "ActionFind"] +__all__ = [ + "ResponseFunctionWebSearch", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFindInPage", +] class ActionSearchSource(BaseModel): @@ -45,20 +52,20 @@ class ActionOpenPage(BaseModel): """The URL opened by the model.""" -class ActionFind(BaseModel): - """Action type "find": Searches for a pattern within a loaded page.""" +class ActionFindInPage(BaseModel): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" pattern: str """The pattern or text to search for within the page.""" - type: Literal["find"] + type: Literal["find_in_page"] """The action type.""" url: str """The URL of the page searched for the pattern.""" -Action: TypeAlias = Annotated[Union[ActionSearch, ActionOpenPage, ActionFind], PropertyInfo(discriminator="type")] +Action: TypeAlias = Annotated[Union[ActionSearch, ActionOpenPage, ActionFindInPage], PropertyInfo(discriminator="type")] class ResponseFunctionWebSearch(BaseModel): diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 017e35efbc..8eea5598e6 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -13,7 +13,7 @@ "ActionSearch", "ActionSearchSource", "ActionOpenPage", - "ActionFind", + "ActionFindInPage", ] @@ -53,20 +53,20 @@ class ActionOpenPage(TypedDict, total=False): """The URL opened by the model.""" -class ActionFind(TypedDict, total=False): - """Action type "find": Searches for a pattern within a loaded page.""" +class ActionFindInPage(TypedDict, total=False): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" pattern: Required[str] """The pattern or text to search for within the page.""" - type: Required[Literal["find"]] + type: Required[Literal["find_in_page"]] """The action type.""" url: Required[str] """The URL of the page searched for the pattern.""" -Action: TypeAlias = Union[ActionSearch, ActionOpenPage, ActionFind] +Action: TypeAlias = Union[ActionSearch, ActionOpenPage, ActionFindInPage] class ResponseFunctionWebSearchParam(TypedDict, total=False): From 31b4218b71025c9183eb8320629af5de74682adc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 04:21:36 +0000 Subject: [PATCH 220/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index d9e272c17f..6a1f554cc2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c3a646eb5dcad2b7ff7bd976c0e312b886676a542f6ffcd9a6c8503ae24c58.yml openapi_spec_hash: 91b1b7bf3c1a6b6c9c7507d4cac8fe2a -config_hash: f8e6baff429cf000b8e4ba1da08dff47 +config_hash: 3e6ddffe104c0689cd9ef9a7a7863225 From b95c09d3f1b760378ee4137b83a8e9b87156bedd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 15:30:13 +0000 Subject: [PATCH 221/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 6a1f554cc2..d9e272c17f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c3a646eb5dcad2b7ff7bd976c0e312b886676a542f6ffcd9a6c8503ae24c58.yml openapi_spec_hash: 91b1b7bf3c1a6b6c9c7507d4cac8fe2a -config_hash: 3e6ddffe104c0689cd9ef9a7a7863225 +config_hash: f8e6baff429cf000b8e4ba1da08dff47 From b982088450a89409ae0eedc1f27f84f383b447af Mon Sep 17 00:00:00 2001 From: David Meadows Date: Tue, 3 Feb 2026 10:36:36 -0500 Subject: [PATCH 222/408] fix(client): undo change to web search Find action --- src/openai/types/responses/response_function_web_search.py | 6 +++--- .../types/responses/response_function_web_search_param.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index e0a9caf43b..de6001e146 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -12,7 +12,7 @@ "ActionSearch", "ActionSearchSource", "ActionOpenPage", - "ActionFindInPage", + "ActionFind", ] @@ -52,7 +52,7 @@ class ActionOpenPage(BaseModel): """The URL opened by the model.""" -class ActionFindInPage(BaseModel): +class ActionFind(BaseModel): """Action type "find_in_page": Searches for a pattern within a loaded page.""" pattern: str @@ -65,7 +65,7 @@ class ActionFindInPage(BaseModel): """The URL of the page searched for the pattern.""" -Action: TypeAlias = Annotated[Union[ActionSearch, ActionOpenPage, ActionFindInPage], PropertyInfo(discriminator="type")] +Action: TypeAlias = Annotated[Union[ActionSearch, ActionOpenPage, ActionFind], PropertyInfo(discriminator="type")] class ResponseFunctionWebSearch(BaseModel): diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 8eea5598e6..15e313b0d3 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -13,7 +13,7 @@ "ActionSearch", "ActionSearchSource", "ActionOpenPage", - "ActionFindInPage", + "ActionFind", ] @@ -53,7 +53,7 @@ class ActionOpenPage(TypedDict, total=False): """The URL opened by the model.""" -class ActionFindInPage(TypedDict, total=False): +class ActionFind(TypedDict, total=False): """Action type "find_in_page": Searches for a pattern within a loaded page.""" pattern: Required[str] @@ -66,7 +66,7 @@ class ActionFindInPage(TypedDict, total=False): """The URL of the page searched for the pattern.""" -Action: TypeAlias = Union[ActionSearch, ActionOpenPage, ActionFindInPage] +Action: TypeAlias = Union[ActionSearch, ActionOpenPage, ActionFind] class ResponseFunctionWebSearchParam(TypedDict, total=False): From e8888736c86bb1d5a27100867da22b11ab5bb1b7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 05:25:56 +0000 Subject: [PATCH 223/408] release: 2.17.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b258565371..c1a7e63f69 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.16.0" + ".": "2.17.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 28902cb2b2..ea480c424e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 2.17.0 (2026-02-05) + +Full Changelog: [v2.16.0...v2.17.0](https://github.com/openai/openai-python/compare/v2.16.0...v2.17.0) + +### Features + +* **api:** add shell_call_output status field ([1bbaf88](https://github.com/openai/openai-python/commit/1bbaf8865000b338c24c9fdd5e985183feaca10f)) +* **api:** image generation actions for responses; ResponseFunctionCallArgumentsDoneEvent.name ([7d96513](https://github.com/openai/openai-python/commit/7d965135f93f41b0c3dbf3dc9f01796bd9645b6c)) +* **client:** add custom JSON encoder for extended type support ([9f43c8b](https://github.com/openai/openai-python/commit/9f43c8b1a1641db2336cc6d0ec0c6dc470a89103)) + + +### Bug Fixes + +* **client:** undo change to web search Find action ([8f14eb0](https://github.com/openai/openai-python/commit/8f14eb0a74363fdfc648c5cd5c6d34a85b938d3c)) +* **client:** update type for `find_in_page` action ([ec54dde](https://github.com/openai/openai-python/commit/ec54ddeb357e49edd81cc3fe53d549c297e59a07)) + ## 2.16.0 (2026-01-27) Full Changelog: [v2.15.0...v2.16.0](https://github.com/openai/openai-python/compare/v2.15.0...v2.16.0) diff --git a/pyproject.toml b/pyproject.toml index bd75d0096d..e3843e1f56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.16.0" +version = "2.17.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index eb61bdd2c6..5f7901120a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.16.0" # x-release-please-version +__version__ = "2.17.0" # x-release-please-version From a7a60166ad8f686f388719a147f815b053c9e885 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:41:09 -0500 Subject: [PATCH 224/408] release: 2.18.0 (#2846) * codegen metadata * test note * feat(api): add context_management to responses * feat(api): responses context_management * release: 2.18.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: Alex Chang --- .release-please-manifest.json | 2 +- .stats.yml | 6 +-- CHANGELOG.md | 9 +++++ examples/realtime/azure_realtime.py | 5 +-- pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/images.py | 24 +++++------ src/openai/resources/responses/responses.py | 34 ++++++++++++++++ .../resources/vector_stores/file_batches.py | 40 ++++++++++++++++--- src/openai/resources/vector_stores/files.py | 30 +++++++++++++- src/openai/types/image_edit_params.py | 4 +- .../types/responses/response_create_params.py | 12 ++++++ src/openai/types/responses/tool.py | 4 +- src/openai/types/responses/tool_param.py | 4 +- tests/api_resources/test_responses.py | 24 +++++++++++ .../vector_stores/test_file_batches.py | 12 ++++++ .../api_resources/vector_stores/test_files.py | 1 - 17 files changed, 178 insertions(+), 37 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c1a7e63f69..ca3496a3e6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.17.0" + ".": "2.18.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index d9e272c17f..fce116bc4f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c3a646eb5dcad2b7ff7bd976c0e312b886676a542f6ffcd9a6c8503ae24c58.yml -openapi_spec_hash: 91b1b7bf3c1a6b6c9c7507d4cac8fe2a -config_hash: f8e6baff429cf000b8e4ba1da08dff47 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bff810f46da56eff8d5e189b0d1f56ac07a8289723666138549d4239cad7c2ea.yml +openapi_spec_hash: 7532ce5a6f490c8f5d1e079c76c70535 +config_hash: a1454ffd9612dee11f9d5a98e55eac9e diff --git a/CHANGELOG.md b/CHANGELOG.md index ea480c424e..ad2758cf6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 2.18.0 (2026-02-09) + +Full Changelog: [v2.17.0...v2.18.0](https://github.com/openai/openai-python/compare/v2.17.0...v2.18.0) + +### Features + +* **api:** add context_management to responses ([137e992](https://github.com/openai/openai-python/commit/137e992b80956401d1867274fa7a0969edfdba54)) +* **api:** responses context_management ([c3bd017](https://github.com/openai/openai-python/commit/c3bd017318347af0a0105a7e975c8d91e22f7941)) + ## 2.17.0 (2026-02-05) Full Changelog: [v2.16.0...v2.17.0](https://github.com/openai/openai-python/compare/v2.16.0...v2.17.0) diff --git a/examples/realtime/azure_realtime.py b/examples/realtime/azure_realtime.py index 177c24fdfa..a8bcf4c536 100644 --- a/examples/realtime/azure_realtime.py +++ b/examples/realtime/azure_realtime.py @@ -41,10 +41,7 @@ async def main() -> None: # The APIs are compatible with the OpenAI client library. # You can use the OpenAI client library to access the Azure OpenAI APIs. # Make sure to set the baseURL and apiKey to use the Azure OpenAI endpoint and token. - client = AsyncOpenAI( - websocket_base_url=base_url, - api_key=token - ) + client = AsyncOpenAI(websocket_base_url=base_url, api_key=token) async with client.realtime.connect( model=deployment_name, ) as connection: diff --git a/pyproject.toml b/pyproject.toml index e3843e1f56..4dc3418464 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.17.0" +version = "2.18.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 5f7901120a..aede964362 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.17.0" # x-release-please-version +__version__ = "2.18.0" # x-release-please-version diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 805828488f..3de5185a87 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -172,8 +172,8 @@ def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -291,8 +291,8 @@ def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -406,8 +406,8 @@ def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1068,8 +1068,8 @@ async def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1187,8 +1187,8 @@ async def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, @@ -1302,8 +1302,8 @@ async def edit( input_fidelity: Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. mask: An additional image whose fully transparent areas (e.g. where alpha is zero) indicate where `image` should be edited. If there are multiple images provided, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 8e80f6793b..79034b7e18 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -95,6 +95,7 @@ def create( self, *, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -147,6 +148,8 @@ def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + context_management: Context management configuration for this request. + conversation: The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request. Input items and output items from this response are automatically added to this conversation after this @@ -341,6 +344,7 @@ def create( *, stream: Literal[True], background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -399,6 +403,8 @@ def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + context_management: Context management configuration for this request. + conversation: The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request. Input items and output items from this response are automatically added to this conversation after this @@ -586,6 +592,7 @@ def create( *, stream: bool, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -644,6 +651,8 @@ def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + context_management: Context management configuration for this request. + conversation: The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request. Input items and output items from this response are automatically added to this conversation after this @@ -829,6 +838,7 @@ def create( self, *, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -868,6 +878,7 @@ def create( body=maybe_transform( { "background": background, + "context_management": context_management, "conversation": conversation, "include": include, "input": input, @@ -930,6 +941,7 @@ def stream( input: Union[str, ResponseInputParam], model: ResponsesModel, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, text_format: type[TextFormatT] | Omit = omit, tools: Iterable[ParseableToolParam] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, @@ -970,6 +982,7 @@ def stream( input: Union[str, ResponseInputParam] | Omit = omit, model: ResponsesModel | Omit = omit, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, text_format: type[TextFormatT] | Omit = omit, tools: Iterable[ParseableToolParam] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, @@ -1006,6 +1019,7 @@ def stream( new_response_args = { "input": input, "model": model, + "context_management": context_management, "conversation": conversation, "include": include, "instructions": instructions, @@ -1061,6 +1075,7 @@ def stream( input=input, model=model, tools=tools, + context_management=context_management, conversation=conversation, include=include, instructions=instructions, @@ -1118,6 +1133,7 @@ def parse( *, text_format: type[TextFormatT] | Omit = omit, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -1176,6 +1192,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: body=maybe_transform( { "background": background, + "context_management": context_management, "conversation": conversation, "include": include, "input": input, @@ -1709,6 +1726,7 @@ async def create( self, *, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -1761,6 +1779,8 @@ async def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + context_management: Context management configuration for this request. + conversation: The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request. Input items and output items from this response are automatically added to this conversation after this @@ -1955,6 +1975,7 @@ async def create( *, stream: Literal[True], background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -2013,6 +2034,8 @@ async def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + context_management: Context management configuration for this request. + conversation: The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request. Input items and output items from this response are automatically added to this conversation after this @@ -2200,6 +2223,7 @@ async def create( *, stream: bool, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -2258,6 +2282,8 @@ async def create( background: Whether to run the model response in the background. [Learn more](https://platform.openai.com/docs/guides/background). + context_management: Context management configuration for this request. + conversation: The conversation that this response belongs to. Items from this conversation are prepended to `input_items` for this response request. Input items and output items from this response are automatically added to this conversation after this @@ -2443,6 +2469,7 @@ async def create( self, *, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -2482,6 +2509,7 @@ async def create( body=await async_maybe_transform( { "background": background, + "context_management": context_management, "conversation": conversation, "include": include, "input": input, @@ -2544,6 +2572,7 @@ def stream( input: Union[str, ResponseInputParam], model: ResponsesModel, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, text_format: type[TextFormatT] | Omit = omit, tools: Iterable[ParseableToolParam] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, @@ -2584,6 +2613,7 @@ def stream( input: Union[str, ResponseInputParam] | Omit = omit, model: ResponsesModel | Omit = omit, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, text_format: type[TextFormatT] | Omit = omit, tools: Iterable[ParseableToolParam] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, @@ -2620,6 +2650,7 @@ def stream( new_response_args = { "input": input, "model": model, + "context_management": context_management, "conversation": conversation, "include": include, "instructions": instructions, @@ -2675,6 +2706,7 @@ def stream( model=model, stream=True, tools=tools, + context_management=context_management, conversation=conversation, include=include, instructions=instructions, @@ -2736,6 +2768,7 @@ async def parse( *, text_format: type[TextFormatT] | Omit = omit, background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, conversation: Optional[response_create_params.Conversation] | Omit = omit, include: Optional[List[ResponseIncludable]] | Omit = omit, input: Union[str, ResponseInputParam] | Omit = omit, @@ -2794,6 +2827,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: body=maybe_transform( { "background": background, + "context_management": context_management, "conversation": conversation, "include": include, "input": input, diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index d31fb59bec..fca1ef89fa 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -194,15 +194,29 @@ def create_and_poll( self, vector_store_id: str, *, - file_ids: SequenceNotStr[str], - poll_interval_ms: int | Omit = omit, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = self.create( vector_store_id=vector_store_id, - file_ids=file_ids, + attributes=attributes, chunking_strategy=chunking_strategy, + file_ids=file_ids, + files=files, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ) # TODO: don't poll unless necessary?? return self.poll( @@ -539,15 +553,29 @@ async def create_and_poll( self, vector_store_id: str, *, - file_ids: SequenceNotStr[str], - poll_interval_ms: int | Omit = omit, + attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + file_ids: SequenceNotStr[str] | Omit = omit, + files: Iterable[file_batch_create_params.File] | Omit = omit, + poll_interval_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFileBatch: """Create a vector store batch and poll until all files have been processed.""" batch = await self.create( vector_store_id=vector_store_id, - file_ids=file_ids, + attributes=attributes, chunking_strategy=chunking_strategy, + file_ids=file_ids, + files=files, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ) # TODO: don't poll unless necessary?? return await self.poll( diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py index d2eb4e16ed..29f6e879f1 100644 --- a/src/openai/resources/vector_stores/files.py +++ b/src/openai/resources/vector_stores/files.py @@ -307,10 +307,23 @@ def create_and_poll( attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, poll_interval_ms: int | Omit = omit, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" self.create( - vector_store_id=vector_store_id, file_id=file_id, chunking_strategy=chunking_strategy, attributes=attributes + vector_store_id=vector_store_id, + file_id=file_id, + chunking_strategy=chunking_strategy, + attributes=attributes, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ) return self.poll( @@ -715,10 +728,23 @@ async def create_and_poll( attributes: Optional[Dict[str, Union[str, float, bool]]] | Omit = omit, poll_interval_ms: int | Omit = omit, chunking_strategy: FileChunkingStrategyParam | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VectorStoreFile: """Attach a file to the given vector store and wait for it to be processed.""" await self.create( - vector_store_id=vector_store_id, file_id=file_id, chunking_strategy=chunking_strategy, attributes=attributes + vector_store_id=vector_store_id, + file_id=file_id, + chunking_strategy=chunking_strategy, + attributes=attributes, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ) return await self.poll( diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 0bd5f39fac..916fbc1411 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -45,8 +45,8 @@ class ImageEditParamsBase(TypedDict, total=False): """ Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. """ mask: FileTypes diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 15844c6597..97aaf9dc3a 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -25,6 +25,7 @@ __all__ = [ "ResponseCreateParamsBase", + "ContextManagement", "Conversation", "StreamOptions", "ToolChoice", @@ -40,6 +41,9 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/background). """ + context_management: Optional[Iterable[ContextManagement]] + """Context management configuration for this request.""" + conversation: Optional[Conversation] """The conversation that this response belongs to. @@ -279,6 +283,14 @@ class ResponseCreateParamsBase(TypedDict, total=False): """ +class ContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + Conversation: TypeAlias = Union[str, ResponseConversationParam] diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 435f046768..bd266ea753 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -240,8 +240,8 @@ class ImageGeneration(BaseModel): """ Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. """ input_image_mask: Optional[ImageGenerationInputImageMask] = None diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 3afff87892..a81c69d3ab 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -240,8 +240,8 @@ class ImageGeneration(TypedDict, total=False): """ Control how much effort the model will exert to match the style and features, especially facial features, of input images. This parameter is only supported - for `gpt-image-1`. Unsupported for `gpt-image-1-mini`. Supports `high` and - `low`. Defaults to `low`. + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. """ input_image_mask: ImageGenerationInputImageMask diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 3cdbdba8a6..a644b2c6b9 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -30,6 +30,12 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: response = client.responses.create( background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], conversation="string", include=["file_search_call.results"], input="string", @@ -111,6 +117,12 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: response_stream = client.responses.create( stream=True, background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], conversation="string", include=["file_search_call.results"], input="string", @@ -426,6 +438,12 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.create( background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], conversation="string", include=["file_search_call.results"], input="string", @@ -507,6 +525,12 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn response_stream = await async_client.responses.create( stream=True, background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], conversation="string", include=["file_search_call.results"], input="string", diff --git a/tests/api_resources/vector_stores/test_file_batches.py b/tests/api_resources/vector_stores/test_file_batches.py index abbefc20e9..c1fba534a6 100644 --- a/tests/api_resources/vector_stores/test_file_batches.py +++ b/tests/api_resources/vector_stores/test_file_batches.py @@ -9,6 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type +from openai._utils import assert_signatures_in_sync from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.vector_stores import ( VectorStoreFile, @@ -450,3 +451,14 @@ async def test_path_params_list_files(self, async_client: AsyncOpenAI) -> None: batch_id="", vector_store_id="vector_store_id", ) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: + checking_client: OpenAI | AsyncOpenAI = client if sync else async_client + + # ensure helpers do not drift from generated spec + assert_signatures_in_sync( + checking_client.vector_stores.file_batches.create, + checking_client.vector_stores.file_batches.create_and_poll, + ) diff --git a/tests/api_resources/vector_stores/test_files.py b/tests/api_resources/vector_stores/test_files.py index 7394b50d95..53aa5ee041 100644 --- a/tests/api_resources/vector_stores/test_files.py +++ b/tests/api_resources/vector_stores/test_files.py @@ -635,7 +635,6 @@ def test_create_and_poll_method_in_sync(sync: bool, client: OpenAI, async_client assert_signatures_in_sync( checking_client.vector_stores.files.create, checking_client.vector_stores.files.create_and_poll, - exclude_params={"extra_headers", "extra_query", "extra_body", "timeout"}, ) From 57d7fe790c067300ddb02b5482b41fe0ce041769 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:10:49 +0000 Subject: [PATCH 225/408] chore(internal): bump dependencies --- requirements-dev.lock | 20 ++++++++++++++------ requirements.lock | 8 ++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index a7201a127b..b4ed71101a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,14 +12,14 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.2 +aiohttp==3.13.3 # via httpx-aiohttp # via openai aiosignal==1.4.0 # via aiohttp annotated-types==0.7.0 # via pydantic -anyio==4.12.0 +anyio==4.12.1 # via httpx # via openai argcomplete==3.6.3 @@ -38,7 +38,7 @@ azure-core==1.36.0 azure-identity==1.25.1 backports-asyncio-runner==1.2.0 # via pytest-asyncio -certifi==2025.11.12 +certifi==2026.1.4 # via httpcore # via httpx # via requests @@ -84,7 +84,7 @@ httpx==0.28.1 # via httpx-aiohttp # via openai # via respx -httpx-aiohttp==0.1.9 +httpx-aiohttp==0.1.12 # via openai humanize==4.13.0 # via nox @@ -94,7 +94,7 @@ idna==3.11 # via requests # via trio # via yarl -importlib-metadata==8.7.0 +importlib-metadata==8.7.1 iniconfig==2.1.0 # via pytest inline-snapshot==0.31.1 @@ -170,8 +170,12 @@ requests==2.32.5 # via msal respx==0.22.0 rich==14.2.0 +<<<<<<< HEAD # via inline-snapshot ruff==0.14.7 +======= +ruff==0.14.13 +>>>>>>> origin/generated--merge-conflict six==1.17.0 # via python-dateutil sniffio==1.3.1 @@ -182,7 +186,7 @@ sortedcontainers==2.4.0 sounddevice==0.5.3 # via openai time-machine==2.19.0 -tomli==2.3.0 +tomli==2.4.0 # via dependency-groups # via inline-snapshot # via mypy @@ -215,12 +219,16 @@ typing-extensions==4.15.0 # via virtualenv typing-inspection==0.4.2 # via pydantic +<<<<<<< HEAD tzdata==2025.2 # via pandas urllib3==2.5.0 # via requests # via types-requests virtualenv==20.35.4 +======= +virtualenv==20.36.1 +>>>>>>> origin/generated--merge-conflict # via nox websockets==15.0.1 # via openai diff --git a/requirements.lock b/requirements.lock index 8e021bd69b..af6e4f99e8 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,21 +12,21 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.2 +aiohttp==3.13.3 # via httpx-aiohttp # via openai aiosignal==1.4.0 # via aiohttp annotated-types==0.7.0 # via pydantic -anyio==4.12.0 +anyio==4.12.1 # via httpx # via openai async-timeout==5.0.1 # via aiohttp attrs==25.4.0 # via aiohttp -certifi==2025.11.12 +certifi==2026.1.4 # via httpcore # via httpx cffi==2.0.0 @@ -45,7 +45,7 @@ httpcore==1.0.9 httpx==0.28.1 # via httpx-aiohttp # via openai -httpx-aiohttp==0.1.9 +httpx-aiohttp==0.1.12 # via openai idna==3.11 # via anyio From b3c7207e63c8d961f0b642f574e69f0e947a090b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:05:29 +0000 Subject: [PATCH 226/408] feat(api): skills and hosted shell --- .stats.yml | 8 +- api.md | 55 ++ src/openai/__init__.py | 1 + src/openai/_client.py | 38 ++ src/openai/_module_client.py | 8 + src/openai/resources/__init__.py | 14 + src/openai/resources/images.py | 108 ++- src/openai/resources/skills/__init__.py | 47 ++ src/openai/resources/skills/content.py | 167 +++++ src/openai/resources/skills/skills.py | 618 ++++++++++++++++++ .../resources/skills/versions/__init__.py | 33 + .../resources/skills/versions/content.py | 177 +++++ .../resources/skills/versions/versions.py | 544 +++++++++++++++ src/openai/types/__init__.py | 6 + src/openai/types/deleted_skill.py | 15 + src/openai/types/image_edit_params.py | 19 +- src/openai/types/responses/__init__.py | 30 + src/openai/types/responses/container_auto.py | 36 + .../types/responses/container_auto_param.py | 35 + .../container_network_policy_allowlist.py | 20 + ...ontainer_network_policy_allowlist_param.py | 22 + .../container_network_policy_disabled.py | 12 + ...container_network_policy_disabled_param.py | 12 + .../container_network_policy_domain_secret.py | 16 + ...iner_network_policy_domain_secret_param.py | 18 + .../types/responses/container_reference.py | 15 + .../responses/container_reference_param.py | 15 + .../types/responses/function_shell_tool.py | 15 +- .../responses/function_shell_tool_param.py | 13 +- src/openai/types/responses/inline_skill.py | 22 + .../types/responses/inline_skill_param.py | 23 + .../types/responses/inline_skill_source.py | 20 + .../responses/inline_skill_source_param.py | 20 + .../types/responses/local_environment.py | 17 + .../responses/local_environment_param.py | 18 + src/openai/types/responses/local_skill.py | 16 + .../types/responses/local_skill_param.py | 18 + .../responses/response_container_reference.py | 16 + .../response_function_shell_tool_call.py | 17 +- .../types/responses/response_input_item.py | 11 + .../responses/response_input_item_param.py | 9 + .../types/responses/response_input_param.py | 9 + .../responses/response_local_environment.py | 14 + src/openai/types/responses/skill_reference.py | 19 + .../types/responses/skill_reference_param.py | 18 + src/openai/types/responses/tool.py | 11 + src/openai/types/responses/tool_param.py | 11 + src/openai/types/skill.py | 30 + src/openai/types/skill_create_params.py | 15 + src/openai/types/skill_list.py | 26 + src/openai/types/skill_list_params.py | 21 + src/openai/types/skill_update_params.py | 12 + src/openai/types/skills/__init__.py | 9 + .../types/skills/deleted_skill_version.py | 18 + src/openai/types/skills/skill_version.py | 30 + src/openai/types/skills/skill_version_list.py | 26 + .../types/skills/version_create_params.py | 18 + .../types/skills/version_list_params.py | 18 + src/openai/types/skills/versions/__init__.py | 3 + tests/api_resources/skills/__init__.py | 1 + tests/api_resources/skills/test_content.py | 122 ++++ tests/api_resources/skills/test_versions.py | 407 ++++++++++++ .../api_resources/skills/versions/__init__.py | 1 + .../skills/versions/test_content.py | 154 +++++ tests/api_resources/test_skills.py | 393 +++++++++++ 65 files changed, 3628 insertions(+), 82 deletions(-) create mode 100644 src/openai/resources/skills/__init__.py create mode 100644 src/openai/resources/skills/content.py create mode 100644 src/openai/resources/skills/skills.py create mode 100644 src/openai/resources/skills/versions/__init__.py create mode 100644 src/openai/resources/skills/versions/content.py create mode 100644 src/openai/resources/skills/versions/versions.py create mode 100644 src/openai/types/deleted_skill.py create mode 100644 src/openai/types/responses/container_auto.py create mode 100644 src/openai/types/responses/container_auto_param.py create mode 100644 src/openai/types/responses/container_network_policy_allowlist.py create mode 100644 src/openai/types/responses/container_network_policy_allowlist_param.py create mode 100644 src/openai/types/responses/container_network_policy_disabled.py create mode 100644 src/openai/types/responses/container_network_policy_disabled_param.py create mode 100644 src/openai/types/responses/container_network_policy_domain_secret.py create mode 100644 src/openai/types/responses/container_network_policy_domain_secret_param.py create mode 100644 src/openai/types/responses/container_reference.py create mode 100644 src/openai/types/responses/container_reference_param.py create mode 100644 src/openai/types/responses/inline_skill.py create mode 100644 src/openai/types/responses/inline_skill_param.py create mode 100644 src/openai/types/responses/inline_skill_source.py create mode 100644 src/openai/types/responses/inline_skill_source_param.py create mode 100644 src/openai/types/responses/local_environment.py create mode 100644 src/openai/types/responses/local_environment_param.py create mode 100644 src/openai/types/responses/local_skill.py create mode 100644 src/openai/types/responses/local_skill_param.py create mode 100644 src/openai/types/responses/response_container_reference.py create mode 100644 src/openai/types/responses/response_local_environment.py create mode 100644 src/openai/types/responses/skill_reference.py create mode 100644 src/openai/types/responses/skill_reference_param.py create mode 100644 src/openai/types/skill.py create mode 100644 src/openai/types/skill_create_params.py create mode 100644 src/openai/types/skill_list.py create mode 100644 src/openai/types/skill_list_params.py create mode 100644 src/openai/types/skill_update_params.py create mode 100644 src/openai/types/skills/__init__.py create mode 100644 src/openai/types/skills/deleted_skill_version.py create mode 100644 src/openai/types/skills/skill_version.py create mode 100644 src/openai/types/skills/skill_version_list.py create mode 100644 src/openai/types/skills/version_create_params.py create mode 100644 src/openai/types/skills/version_list_params.py create mode 100644 src/openai/types/skills/versions/__init__.py create mode 100644 tests/api_resources/skills/__init__.py create mode 100644 tests/api_resources/skills/test_content.py create mode 100644 tests/api_resources/skills/test_versions.py create mode 100644 tests/api_resources/skills/versions/__init__.py create mode 100644 tests/api_resources/skills/versions/test_content.py create mode 100644 tests/api_resources/test_skills.py diff --git a/.stats.yml b/.stats.yml index fce116bc4f..e91271a6c8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 137 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bff810f46da56eff8d5e189b0d1f56ac07a8289723666138549d4239cad7c2ea.yml -openapi_spec_hash: 7532ce5a6f490c8f5d1e079c76c70535 -config_hash: a1454ffd9612dee11f9d5a98e55eac9e +configured_endpoints: 148 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7debfce2217c66ea87bfddaa244b57de4062dd7fd766fa9a28e869614205c040.yml +openapi_spec_hash: e910fc478b8449134e2af1dc15fd33f7 +config_hash: 8dca0f2dc2706c07cf2f8d0ed4dc062e diff --git a/api.md b/api.md index 1c054b52fc..54fa94da5f 100644 --- a/api.md +++ b/api.md @@ -736,11 +736,20 @@ from openai.types.responses import ( ApplyPatchTool, CompactedResponse, ComputerTool, + ContainerAuto, + ContainerNetworkPolicyAllowlist, + ContainerNetworkPolicyDisabled, + ContainerNetworkPolicyDomainSecret, + ContainerReference, CustomTool, EasyInputMessage, FileSearchTool, FunctionShellTool, FunctionTool, + InlineSkill, + InlineSkillSource, + LocalEnvironment, + LocalSkill, Response, ResponseApplyPatchToolCall, ResponseApplyPatchToolCallOutput, @@ -760,6 +769,7 @@ from openai.types.responses import ( ResponseComputerToolCall, ResponseComputerToolCallOutputItem, ResponseComputerToolCallOutputScreenshot, + ResponseContainerReference, ResponseContent, ResponseContentPartAddedEvent, ResponseContentPartDoneEvent, @@ -809,6 +819,7 @@ from openai.types.responses import ( ResponseInputText, ResponseInputTextContent, ResponseItem, + ResponseLocalEnvironment, ResponseMcpCallArgumentsDeltaEvent, ResponseMcpCallArgumentsDoneEvent, ResponseMcpCallCompletedEvent, @@ -845,6 +856,7 @@ from openai.types.responses import ( ResponseWebSearchCallCompletedEvent, ResponseWebSearchCallInProgressEvent, ResponseWebSearchCallSearchingEvent, + SkillReference, Tool, ToolChoiceAllowed, ToolChoiceApplyPatch, @@ -1170,6 +1182,49 @@ Methods: - client.containers.files.content.retrieve(file_id, \*, container_id) -> HttpxBinaryResponseContent +# Skills + +Types: + +```python +from openai.types import DeletedSkill, Skill, SkillList +``` + +Methods: + +- client.skills.create(\*\*params) -> Skill +- client.skills.retrieve(skill_id) -> Skill +- client.skills.update(skill_id, \*\*params) -> Skill +- client.skills.list(\*\*params) -> SyncCursorPage[Skill] +- client.skills.delete(skill_id) -> DeletedSkill + +## Content + +Methods: + +- client.skills.content.retrieve(skill_id) -> HttpxBinaryResponseContent + +## Versions + +Types: + +```python +from openai.types.skills import DeletedSkillVersion, SkillVersion, SkillVersionList +``` + +Methods: + +- client.skills.versions.create(skill_id, \*\*params) -> SkillVersion +- client.skills.versions.retrieve(version, \*, skill_id) -> SkillVersion +- client.skills.versions.list(skill_id, \*\*params) -> SyncCursorPage[SkillVersion] +- client.skills.versions.delete(version, \*, skill_id) -> DeletedSkillVersion + +### Content + +Methods: + +- client.skills.versions.content.retrieve(version, \*, skill_id) -> HttpxBinaryResponseContent + # Videos Types: diff --git a/src/openai/__init__.py b/src/openai/__init__.py index e7411b3886..b2093ada68 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -379,6 +379,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] files as files, images as images, models as models, + skills as skills, videos as videos, batches as batches, uploads as uploads, diff --git a/src/openai/_client.py b/src/openai/_client.py index a3b01b2ce6..440a8a45c8 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -44,6 +44,7 @@ files, images, models, + skills, videos, batches, uploads, @@ -70,6 +71,7 @@ from .resources.completions import Completions, AsyncCompletions from .resources.evals.evals import Evals, AsyncEvals from .resources.moderations import Moderations, AsyncModerations + from .resources.skills.skills import Skills, AsyncSkills from .resources.uploads.uploads import Uploads, AsyncUploads from .resources.realtime.realtime import Realtime, AsyncRealtime from .resources.responses.responses import Responses, AsyncResponses @@ -290,6 +292,12 @@ def containers(self) -> Containers: return Containers(self) + @cached_property + def skills(self) -> Skills: + from .resources.skills import Skills + + return Skills(self) + @cached_property def videos(self) -> Videos: from .resources.videos import Videos @@ -641,6 +649,12 @@ def containers(self) -> AsyncContainers: return AsyncContainers(self) + @cached_property + def skills(self) -> AsyncSkills: + from .resources.skills import AsyncSkills + + return AsyncSkills(self) + @cached_property def videos(self) -> AsyncVideos: from .resources.videos import AsyncVideos @@ -897,6 +911,12 @@ def containers(self) -> containers.ContainersWithRawResponse: return ContainersWithRawResponse(self._client.containers) + @cached_property + def skills(self) -> skills.SkillsWithRawResponse: + from .resources.skills import SkillsWithRawResponse + + return SkillsWithRawResponse(self._client.skills) + @cached_property def videos(self) -> videos.VideosWithRawResponse: from .resources.videos import VideosWithRawResponse @@ -1018,6 +1038,12 @@ def containers(self) -> containers.AsyncContainersWithRawResponse: return AsyncContainersWithRawResponse(self._client.containers) + @cached_property + def skills(self) -> skills.AsyncSkillsWithRawResponse: + from .resources.skills import AsyncSkillsWithRawResponse + + return AsyncSkillsWithRawResponse(self._client.skills) + @cached_property def videos(self) -> videos.AsyncVideosWithRawResponse: from .resources.videos import AsyncVideosWithRawResponse @@ -1139,6 +1165,12 @@ def containers(self) -> containers.ContainersWithStreamingResponse: return ContainersWithStreamingResponse(self._client.containers) + @cached_property + def skills(self) -> skills.SkillsWithStreamingResponse: + from .resources.skills import SkillsWithStreamingResponse + + return SkillsWithStreamingResponse(self._client.skills) + @cached_property def videos(self) -> videos.VideosWithStreamingResponse: from .resources.videos import VideosWithStreamingResponse @@ -1260,6 +1292,12 @@ def containers(self) -> containers.AsyncContainersWithStreamingResponse: return AsyncContainersWithStreamingResponse(self._client.containers) + @cached_property + def skills(self) -> skills.AsyncSkillsWithStreamingResponse: + from .resources.skills import AsyncSkillsWithStreamingResponse + + return AsyncSkillsWithStreamingResponse(self._client.skills) + @cached_property def videos(self) -> videos.AsyncVideosWithStreamingResponse: from .resources.videos import AsyncVideosWithStreamingResponse diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index d0d721887b..17bb9306aa 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -19,6 +19,7 @@ from .resources.completions import Completions from .resources.evals.evals import Evals from .resources.moderations import Moderations + from .resources.skills.skills import Skills from .resources.uploads.uploads import Uploads from .resources.realtime.realtime import Realtime from .resources.responses.responses import Responses @@ -73,6 +74,12 @@ def __load__(self) -> Models: return _load_client().models +class SkillsProxy(LazyProxy["Skills"]): + @override + def __load__(self) -> Skills: + return _load_client().skills + + class VideosProxy(LazyProxy["Videos"]): @override def __load__(self) -> Videos: @@ -158,6 +165,7 @@ def __load__(self) -> Conversations: evals: Evals = EvalsProxy().__as_proxied__() images: Images = ImagesProxy().__as_proxied__() models: Models = ModelsProxy().__as_proxied__() +skills: Skills = SkillsProxy().__as_proxied__() videos: Videos = VideosProxy().__as_proxied__() batches: Batches = BatchesProxy().__as_proxied__() uploads: Uploads = UploadsProxy().__as_proxied__() diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index b793fbc7b0..ed030f7188 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -56,6 +56,14 @@ ModelsWithStreamingResponse, AsyncModelsWithStreamingResponse, ) +from .skills import ( + Skills, + AsyncSkills, + SkillsWithRawResponse, + AsyncSkillsWithRawResponse, + SkillsWithStreamingResponse, + AsyncSkillsWithStreamingResponse, +) from .videos import ( Videos, AsyncVideos, @@ -220,6 +228,12 @@ "AsyncContainersWithRawResponse", "ContainersWithStreamingResponse", "AsyncContainersWithStreamingResponse", + "Skills", + "AsyncSkills", + "SkillsWithRawResponse", + "AsyncSkillsWithRawResponse", + "SkillsWithStreamingResponse", + "AsyncSkillsWithStreamingResponse", "Videos", "AsyncVideos", "VideosWithRawResponse", diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 3de5185a87..647eb1ca24 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -147,14 +147,15 @@ def edit( prompt. This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, - and `gpt-image-1-mini`) and `dall-e-2`. + `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. + 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same + input constraints as GPT image models. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -180,9 +181,7 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and the GPT image models - are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT - image models is used. + model: The model to use for image generation. Defaults to `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -201,14 +200,13 @@ def edit( Note that the final image may be sent before the full number of partial images are generated if the full image is generated more quickly. - quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for the GPT image models. `dall-e-2` only supports `standard` - quality. Defaults to `auto`. + quality: The quality of the image that will be generated for GPT image models. Defaults + to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as the GPT image - models always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image @@ -262,14 +260,15 @@ def edit( prompt. This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, - and `gpt-image-1-mini`) and `dall-e-2`. + `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. + 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same + input constraints as GPT image models. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -299,9 +298,7 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and the GPT image models - are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT - image models is used. + model: The model to use for image generation. Defaults to `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -320,14 +317,13 @@ def edit( Note that the final image may be sent before the full number of partial images are generated if the full image is generated more quickly. - quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for the GPT image models. `dall-e-2` only supports `standard` - quality. Defaults to `auto`. + quality: The quality of the image that will be generated for GPT image models. Defaults + to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as the GPT image - models always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image @@ -377,14 +373,15 @@ def edit( prompt. This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, - and `gpt-image-1-mini`) and `dall-e-2`. + `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. + 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same + input constraints as GPT image models. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -414,9 +411,7 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and the GPT image models - are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT - image models is used. + model: The model to use for image generation. Defaults to `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -435,14 +430,13 @@ def edit( Note that the final image may be sent before the full number of partial images are generated if the full image is generated more quickly. - quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for the GPT image models. `dall-e-2` only supports `standard` - quality. Defaults to `auto`. + quality: The quality of the image that will be generated for GPT image models. Defaults + to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as the GPT image - models always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image @@ -1043,14 +1037,15 @@ async def edit( prompt. This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, - and `gpt-image-1-mini`) and `dall-e-2`. + `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. + 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same + input constraints as GPT image models. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -1076,9 +1071,7 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and the GPT image models - are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT - image models is used. + model: The model to use for image generation. Defaults to `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -1097,14 +1090,13 @@ async def edit( Note that the final image may be sent before the full number of partial images are generated if the full image is generated more quickly. - quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for the GPT image models. `dall-e-2` only supports `standard` - quality. Defaults to `auto`. + quality: The quality of the image that will be generated for GPT image models. Defaults + to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as the GPT image - models always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image @@ -1158,14 +1150,15 @@ async def edit( prompt. This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, - and `gpt-image-1-mini`) and `dall-e-2`. + `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. + 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same + input constraints as GPT image models. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -1195,9 +1188,7 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and the GPT image models - are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT - image models is used. + model: The model to use for image generation. Defaults to `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -1216,14 +1207,13 @@ async def edit( Note that the final image may be sent before the full number of partial images are generated if the full image is generated more quickly. - quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for the GPT image models. `dall-e-2` only supports `standard` - quality. Defaults to `auto`. + quality: The quality of the image that will be generated for GPT image models. Defaults + to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as the GPT image - models always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image @@ -1273,14 +1263,15 @@ async def edit( prompt. This endpoint supports GPT Image models (`gpt-image-1.5`, `gpt-image-1`, - and `gpt-image-1-mini`) and `dall-e-2`. + `gpt-image-1-mini`, and `chatgpt-image-latest`) and `dall-e-2`. Args: image: The image(s) to edit. Must be a supported image file or an array of images. For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. + 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same + input constraints as GPT image models. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -1310,9 +1301,7 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Only `dall-e-2` and the GPT image models - are supported. Defaults to `dall-e-2` unless a parameter specific to the GPT - image models is used. + model: The model to use for image generation. Defaults to `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -1331,14 +1320,13 @@ async def edit( Note that the final image may be sent before the full number of partial images are generated if the full image is generated more quickly. - quality: The quality of the image that will be generated. `high`, `medium` and `low` are - only supported for the GPT image models. `dall-e-2` only supports `standard` - quality. Defaults to `auto`. + quality: The quality of the image that will be generated for GPT image models. Defaults + to `auto`. response_format: The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the image has been - generated. This parameter is only supported for `dall-e-2`, as the GPT image - models always return base64-encoded images. + generated. This parameter is only supported for `dall-e-2` (default is `url` for + `dall-e-2`), as GPT image models always return base64-encoded images. size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image diff --git a/src/openai/resources/skills/__init__.py b/src/openai/resources/skills/__init__.py new file mode 100644 index 0000000000..07f4d6729d --- /dev/null +++ b/src/openai/resources/skills/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .skills import ( + Skills, + AsyncSkills, + SkillsWithRawResponse, + AsyncSkillsWithRawResponse, + SkillsWithStreamingResponse, + AsyncSkillsWithStreamingResponse, +) +from .content import ( + Content, + AsyncContent, + ContentWithRawResponse, + AsyncContentWithRawResponse, + ContentWithStreamingResponse, + AsyncContentWithStreamingResponse, +) +from .versions import ( + Versions, + AsyncVersions, + VersionsWithRawResponse, + AsyncVersionsWithRawResponse, + VersionsWithStreamingResponse, + AsyncVersionsWithStreamingResponse, +) + +__all__ = [ + "Content", + "AsyncContent", + "ContentWithRawResponse", + "AsyncContentWithRawResponse", + "ContentWithStreamingResponse", + "AsyncContentWithStreamingResponse", + "Versions", + "AsyncVersions", + "VersionsWithRawResponse", + "AsyncVersionsWithRawResponse", + "VersionsWithStreamingResponse", + "AsyncVersionsWithStreamingResponse", + "Skills", + "AsyncSkills", + "SkillsWithRawResponse", + "AsyncSkillsWithRawResponse", + "SkillsWithStreamingResponse", + "AsyncSkillsWithStreamingResponse", +] diff --git a/src/openai/resources/skills/content.py b/src/openai/resources/skills/content.py new file mode 100644 index 0000000000..98c1531a94 --- /dev/null +++ b/src/openai/resources/skills/content.py @@ -0,0 +1,167 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ... import _legacy_response +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ..._base_client import make_request_options + +__all__ = ["Content", "AsyncContent"] + + +class Content(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ContentWithStreamingResponse(self) + + def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Get Skill Content + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + f"/skills/{skill_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class AsyncContent(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncContentWithStreamingResponse(self) + + async def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Get Skill Content + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + f"/skills/{skill_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class ContentWithRawResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = _legacy_response.to_raw_response_wrapper( + content.retrieve, + ) + + +class AsyncContentWithRawResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + content.retrieve, + ) + + +class ContentWithStreamingResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = to_custom_streamed_response_wrapper( + content.retrieve, + StreamedBinaryAPIResponse, + ) + + +class AsyncContentWithStreamingResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = async_to_custom_streamed_response_wrapper( + content.retrieve, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/src/openai/resources/skills/skills.py b/src/openai/resources/skills/skills.py new file mode 100644 index 0000000000..b0e929bccf --- /dev/null +++ b/src/openai/resources/skills/skills.py @@ -0,0 +1,618 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Mapping, cast +from typing_extensions import Literal + +import httpx + +from ... import _legacy_response +from ...types import skill_list_params, skill_create_params, skill_update_params +from .content import ( + Content, + AsyncContent, + ContentWithRawResponse, + AsyncContentWithRawResponse, + ContentWithStreamingResponse, + AsyncContentWithStreamingResponse, +) +from ..._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...pagination import SyncCursorPage, AsyncCursorPage +from ...types.skill import Skill +from ..._base_client import AsyncPaginator, make_request_options +from .versions.versions import ( + Versions, + AsyncVersions, + VersionsWithRawResponse, + AsyncVersionsWithRawResponse, + VersionsWithStreamingResponse, + AsyncVersionsWithStreamingResponse, +) +from ...types.deleted_skill import DeletedSkill + +__all__ = ["Skills", "AsyncSkills"] + + +class Skills(SyncAPIResource): + @cached_property + def content(self) -> Content: + return Content(self._client) + + @cached_property + def versions(self) -> Versions: + return Versions(self._client) + + @cached_property + def with_raw_response(self) -> SkillsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SkillsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SkillsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SkillsWithStreamingResponse(self) + + def create( + self, + *, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Create Skill + + Args: + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal({"files": files}) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/skills", + body=maybe_transform(body, skill_create_params.SkillCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Skill, + ) + + def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Get Skill + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._get( + f"/skills/{skill_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Skill, + ) + + def update( + self, + skill_id: str, + *, + default_version: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Update Skill Default Version + + Args: + default_version: The skill version number to set as default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._post( + f"/skills/{skill_id}", + body=maybe_transform({"default_version": default_version}, skill_update_params.SkillUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Skill, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Skill]: + """ + List Skills + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/skills", + page=SyncCursorPage[Skill], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + skill_list_params.SkillListParams, + ), + ), + model=Skill, + ) + + def delete( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DeletedSkill: + """ + Delete Skill + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._delete( + f"/skills/{skill_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedSkill, + ) + + +class AsyncSkills(AsyncAPIResource): + @cached_property + def content(self) -> AsyncContent: + return AsyncContent(self._client) + + @cached_property + def versions(self) -> AsyncVersions: + return AsyncVersions(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSkillsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSkillsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSkillsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSkillsWithStreamingResponse(self) + + async def create( + self, + *, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Create Skill + + Args: + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal({"files": files}) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/skills", + body=await async_maybe_transform(body, skill_create_params.SkillCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Skill, + ) + + async def retrieve( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Get Skill + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return await self._get( + f"/skills/{skill_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Skill, + ) + + async def update( + self, + skill_id: str, + *, + default_version: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Skill: + """ + Update Skill Default Version + + Args: + default_version: The skill version number to set as default. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return await self._post( + f"/skills/{skill_id}", + body=await async_maybe_transform( + {"default_version": default_version}, skill_update_params.SkillUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Skill, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Skill, AsyncCursorPage[Skill]]: + """ + List Skills + + Args: + after: Identifier for the last item from the previous pagination request + + limit: Number of items to retrieve + + order: Sort order of results by timestamp. Use `asc` for ascending order or `desc` for + descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/skills", + page=AsyncCursorPage[Skill], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + skill_list_params.SkillListParams, + ), + ), + model=Skill, + ) + + async def delete( + self, + skill_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DeletedSkill: + """ + Delete Skill + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return await self._delete( + f"/skills/{skill_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedSkill, + ) + + +class SkillsWithRawResponse: + def __init__(self, skills: Skills) -> None: + self._skills = skills + + self.create = _legacy_response.to_raw_response_wrapper( + skills.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + skills.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + skills.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + skills.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> ContentWithRawResponse: + return ContentWithRawResponse(self._skills.content) + + @cached_property + def versions(self) -> VersionsWithRawResponse: + return VersionsWithRawResponse(self._skills.versions) + + +class AsyncSkillsWithRawResponse: + def __init__(self, skills: AsyncSkills) -> None: + self._skills = skills + + self.create = _legacy_response.async_to_raw_response_wrapper( + skills.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + skills.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + skills.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + skills.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithRawResponse: + return AsyncContentWithRawResponse(self._skills.content) + + @cached_property + def versions(self) -> AsyncVersionsWithRawResponse: + return AsyncVersionsWithRawResponse(self._skills.versions) + + +class SkillsWithStreamingResponse: + def __init__(self, skills: Skills) -> None: + self._skills = skills + + self.create = to_streamed_response_wrapper( + skills.create, + ) + self.retrieve = to_streamed_response_wrapper( + skills.retrieve, + ) + self.update = to_streamed_response_wrapper( + skills.update, + ) + self.list = to_streamed_response_wrapper( + skills.list, + ) + self.delete = to_streamed_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> ContentWithStreamingResponse: + return ContentWithStreamingResponse(self._skills.content) + + @cached_property + def versions(self) -> VersionsWithStreamingResponse: + return VersionsWithStreamingResponse(self._skills.versions) + + +class AsyncSkillsWithStreamingResponse: + def __init__(self, skills: AsyncSkills) -> None: + self._skills = skills + + self.create = async_to_streamed_response_wrapper( + skills.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + skills.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + skills.update, + ) + self.list = async_to_streamed_response_wrapper( + skills.list, + ) + self.delete = async_to_streamed_response_wrapper( + skills.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithStreamingResponse: + return AsyncContentWithStreamingResponse(self._skills.content) + + @cached_property + def versions(self) -> AsyncVersionsWithStreamingResponse: + return AsyncVersionsWithStreamingResponse(self._skills.versions) diff --git a/src/openai/resources/skills/versions/__init__.py b/src/openai/resources/skills/versions/__init__.py new file mode 100644 index 0000000000..c9ad6fbbca --- /dev/null +++ b/src/openai/resources/skills/versions/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .content import ( + Content, + AsyncContent, + ContentWithRawResponse, + AsyncContentWithRawResponse, + ContentWithStreamingResponse, + AsyncContentWithStreamingResponse, +) +from .versions import ( + Versions, + AsyncVersions, + VersionsWithRawResponse, + AsyncVersionsWithRawResponse, + VersionsWithStreamingResponse, + AsyncVersionsWithStreamingResponse, +) + +__all__ = [ + "Content", + "AsyncContent", + "ContentWithRawResponse", + "AsyncContentWithRawResponse", + "ContentWithStreamingResponse", + "AsyncContentWithStreamingResponse", + "Versions", + "AsyncVersions", + "VersionsWithRawResponse", + "AsyncVersionsWithRawResponse", + "VersionsWithStreamingResponse", + "AsyncVersionsWithStreamingResponse", +] diff --git a/src/openai/resources/skills/versions/content.py b/src/openai/resources/skills/versions/content.py new file mode 100644 index 0000000000..4494ca0e2f --- /dev/null +++ b/src/openai/resources/skills/versions/content.py @@ -0,0 +1,177 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, + to_custom_streamed_response_wrapper, + async_to_custom_streamed_response_wrapper, +) +from ...._base_client import make_request_options + +__all__ = ["Content", "AsyncContent"] + + +class Content(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ContentWithStreamingResponse(self) + + def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Get Skill Version Content + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return self._get( + f"/skills/{skill_id}/versions/{version}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class AsyncContent(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncContentWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncContentWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContentWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncContentWithStreamingResponse(self) + + async def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> _legacy_response.HttpxBinaryResponseContent: + """ + Get Skill Version Content + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + extra_headers = {"Accept": "application/binary", **(extra_headers or {})} + return await self._get( + f"/skills/{skill_id}/versions/{version}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=_legacy_response.HttpxBinaryResponseContent, + ) + + +class ContentWithRawResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = _legacy_response.to_raw_response_wrapper( + content.retrieve, + ) + + +class AsyncContentWithRawResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + content.retrieve, + ) + + +class ContentWithStreamingResponse: + def __init__(self, content: Content) -> None: + self._content = content + + self.retrieve = to_custom_streamed_response_wrapper( + content.retrieve, + StreamedBinaryAPIResponse, + ) + + +class AsyncContentWithStreamingResponse: + def __init__(self, content: AsyncContent) -> None: + self._content = content + + self.retrieve = async_to_custom_streamed_response_wrapper( + content.retrieve, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/src/openai/resources/skills/versions/versions.py b/src/openai/resources/skills/versions/versions.py new file mode 100644 index 0000000000..890a20774e --- /dev/null +++ b/src/openai/resources/skills/versions/versions.py @@ -0,0 +1,544 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Mapping, cast +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from .content import ( + Content, + AsyncContent, + ContentWithRawResponse, + AsyncContentWithRawResponse, + ContentWithStreamingResponse, + AsyncContentWithStreamingResponse, +) +from ...._types import ( + Body, + Omit, + Query, + Headers, + NotGiven, + FileTypes, + SequenceNotStr, + omit, + not_given, +) +from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.skills import version_list_params, version_create_params +from ....types.skills.skill_version import SkillVersion +from ....types.skills.deleted_skill_version import DeletedSkillVersion + +__all__ = ["Versions", "AsyncVersions"] + + +class Versions(SyncAPIResource): + @cached_property + def content(self) -> Content: + return Content(self._client) + + @cached_property + def with_raw_response(self) -> VersionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return VersionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> VersionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return VersionsWithStreamingResponse(self) + + def create( + self, + skill_id: str, + *, + default: bool | Omit = omit, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Create Skill Version + + Args: + default: Whether to set this version as the default. + + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + body = deepcopy_minimal( + { + "default": default, + "files": files, + } + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + f"/skills/{skill_id}/versions", + body=maybe_transform(body, version_create_params.VersionCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SkillVersion, + ) + + def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Get Skill Version + + Args: + version: The version number to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return self._get( + f"/skills/{skill_id}/versions/{version}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SkillVersion, + ) + + def list( + self, + skill_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[SkillVersion]: + """ + List Skill Versions + + Args: + after: The skill version ID to start after. + + limit: Number of versions to retrieve. + + order: Sort order of results by version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._get_api_list( + f"/skills/{skill_id}/versions", + page=SyncCursorPage[SkillVersion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + version_list_params.VersionListParams, + ), + ), + model=SkillVersion, + ) + + def delete( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DeletedSkillVersion: + """ + Delete Skill Version + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return self._delete( + f"/skills/{skill_id}/versions/{version}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedSkillVersion, + ) + + +class AsyncVersions(AsyncAPIResource): + @cached_property + def content(self) -> AsyncContent: + return AsyncContent(self._client) + + @cached_property + def with_raw_response(self) -> AsyncVersionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncVersionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncVersionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncVersionsWithStreamingResponse(self) + + async def create( + self, + skill_id: str, + *, + default: bool | Omit = omit, + files: Union[SequenceNotStr[FileTypes], FileTypes] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Create Skill Version + + Args: + default: Whether to set this version as the default. + + files: Skill files to upload (directory upload) or a single zip file. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + body = deepcopy_minimal( + { + "default": default, + "files": files, + } + ) + extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) + if extracted_files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + f"/skills/{skill_id}/versions", + body=await async_maybe_transform(body, version_create_params.VersionCreateParams), + files=extracted_files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SkillVersion, + ) + + async def retrieve( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SkillVersion: + """ + Get Skill Version + + Args: + version: The version number to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return await self._get( + f"/skills/{skill_id}/versions/{version}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SkillVersion, + ) + + def list( + self, + skill_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[SkillVersion, AsyncCursorPage[SkillVersion]]: + """ + List Skill Versions + + Args: + after: The skill version ID to start after. + + limit: Number of versions to retrieve. + + order: Sort order of results by version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + return self._get_api_list( + f"/skills/{skill_id}/versions", + page=AsyncCursorPage[SkillVersion], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + version_list_params.VersionListParams, + ), + ), + model=SkillVersion, + ) + + async def delete( + self, + version: str, + *, + skill_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DeletedSkillVersion: + """ + Delete Skill Version + + Args: + version: The skill version number. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not skill_id: + raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") + if not version: + raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") + return await self._delete( + f"/skills/{skill_id}/versions/{version}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedSkillVersion, + ) + + +class VersionsWithRawResponse: + def __init__(self, versions: Versions) -> None: + self._versions = versions + + self.create = _legacy_response.to_raw_response_wrapper( + versions.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + versions.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + versions.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> ContentWithRawResponse: + return ContentWithRawResponse(self._versions.content) + + +class AsyncVersionsWithRawResponse: + def __init__(self, versions: AsyncVersions) -> None: + self._versions = versions + + self.create = _legacy_response.async_to_raw_response_wrapper( + versions.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + versions.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + versions.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithRawResponse: + return AsyncContentWithRawResponse(self._versions.content) + + +class VersionsWithStreamingResponse: + def __init__(self, versions: Versions) -> None: + self._versions = versions + + self.create = to_streamed_response_wrapper( + versions.create, + ) + self.retrieve = to_streamed_response_wrapper( + versions.retrieve, + ) + self.list = to_streamed_response_wrapper( + versions.list, + ) + self.delete = to_streamed_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> ContentWithStreamingResponse: + return ContentWithStreamingResponse(self._versions.content) + + +class AsyncVersionsWithStreamingResponse: + def __init__(self, versions: AsyncVersions) -> None: + self._versions = versions + + self.create = async_to_streamed_response_wrapper( + versions.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + versions.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + versions.list, + ) + self.delete = async_to_streamed_response_wrapper( + versions.delete, + ) + + @cached_property + def content(self) -> AsyncContentWithStreamingResponse: + return AsyncContentWithStreamingResponse(self._versions.content) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 5eb267e845..9190bc146c 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -5,6 +5,7 @@ from .batch import Batch as Batch from .image import Image as Image from .model import Model as Model +from .skill import Skill as Skill from .video import Video as Video from .shared import ( Metadata as Metadata, @@ -30,6 +31,7 @@ from .chat_model import ChatModel as ChatModel from .completion import Completion as Completion from .moderation import Moderation as Moderation +from .skill_list import SkillList as SkillList from .video_size import VideoSize as VideoSize from .audio_model import AudioModel as AudioModel from .batch_error import BatchError as BatchError @@ -41,6 +43,7 @@ from .file_deleted import FileDeleted as FileDeleted from .file_purpose import FilePurpose as FilePurpose from .vector_store import VectorStore as VectorStore +from .deleted_skill import DeletedSkill as DeletedSkill from .model_deleted import ModelDeleted as ModelDeleted from .video_seconds import VideoSeconds as VideoSeconds from .embedding_model import EmbeddingModel as EmbeddingModel @@ -52,6 +55,7 @@ from .batch_list_params import BatchListParams as BatchListParams from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams +from .skill_list_params import SkillListParams as SkillListParams from .video_list_params import VideoListParams as VideoListParams from .video_model_param import VideoModelParam as VideoModelParam from .eval_create_params import EvalCreateParams as EvalCreateParams @@ -61,6 +65,8 @@ from .video_create_error import VideoCreateError as VideoCreateError from .video_remix_params import VideoRemixParams as VideoRemixParams from .batch_create_params import BatchCreateParams as BatchCreateParams +from .skill_create_params import SkillCreateParams as SkillCreateParams +from .skill_update_params import SkillUpdateParams as SkillUpdateParams from .video_create_params import VideoCreateParams as VideoCreateParams from .batch_request_counts import BatchRequestCounts as BatchRequestCounts from .eval_create_response import EvalCreateResponse as EvalCreateResponse diff --git a/src/openai/types/deleted_skill.py b/src/openai/types/deleted_skill.py new file mode 100644 index 0000000000..6f02f58ca4 --- /dev/null +++ b/src/openai/types/deleted_skill.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["DeletedSkill"] + + +class DeletedSkill(BaseModel): + id: str + + deleted: bool + + object: Literal["skill.deleted"] diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 916fbc1411..05f3401d2d 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -17,7 +17,8 @@ class ImageEditParamsBase(TypedDict, total=False): For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. + 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same + input constraints as GPT image models. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -58,11 +59,7 @@ class ImageEditParamsBase(TypedDict, total=False): """ model: Union[str, ImageModel, None] - """The model to use for image generation. - - Only `dall-e-2` and the GPT image models are supported. Defaults to `dall-e-2` - unless a parameter specific to the GPT image models is used. - """ + """The model to use for image generation. Defaults to `gpt-image-1.5`.""" n: Optional[int] """The number of images to generate. Must be between 1 and 10.""" @@ -93,18 +90,18 @@ class ImageEditParamsBase(TypedDict, total=False): """ quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] - """The quality of the image that will be generated. + """The quality of the image that will be generated for GPT image models. - `high`, `medium` and `low` are only supported for the GPT image models. - `dall-e-2` only supports `standard` quality. Defaults to `auto`. + Defaults to `auto`. """ response_format: Optional[Literal["url", "b64_json"]] """The format in which the generated images are returned. Must be one of `url` or `b64_json`. URLs are only valid for 60 minutes after the - image has been generated. This parameter is only supported for `dall-e-2`, as - the GPT image models always return base64-encoded images. + image has been generated. This parameter is only supported for `dall-e-2` + (default is `url` for `dall-e-2`), as GPT image models always return + base64-encoded images. """ size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index a4d939d9ff..cbb8deae95 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -6,9 +6,12 @@ from .response import Response as Response from .tool_param import ToolParam as ToolParam from .custom_tool import CustomTool as CustomTool +from .local_skill import LocalSkill as LocalSkill +from .inline_skill import InlineSkill as InlineSkill from .computer_tool import ComputerTool as ComputerTool from .function_tool import FunctionTool as FunctionTool from .response_item import ResponseItem as ResponseItem +from .container_auto import ContainerAuto as ContainerAuto from .response_error import ResponseError as ResponseError from .response_usage import ResponseUsage as ResponseUsage from .parsed_response import ( @@ -21,26 +24,33 @@ ) from .response_prompt import ResponsePrompt as ResponsePrompt from .response_status import ResponseStatus as ResponseStatus +from .skill_reference import SkillReference as SkillReference from .tool_choice_mcp import ToolChoiceMcp as ToolChoiceMcp from .web_search_tool import WebSearchTool as WebSearchTool from .apply_patch_tool import ApplyPatchTool as ApplyPatchTool from .file_search_tool import FileSearchTool as FileSearchTool from .custom_tool_param import CustomToolParam as CustomToolParam +from .local_environment import LocalEnvironment as LocalEnvironment +from .local_skill_param import LocalSkillParam as LocalSkillParam from .tool_choice_shell import ToolChoiceShell as ToolChoiceShell from .tool_choice_types import ToolChoiceTypes as ToolChoiceTypes from .compacted_response import CompactedResponse as CompactedResponse from .easy_input_message import EasyInputMessage as EasyInputMessage +from .inline_skill_param import InlineSkillParam as InlineSkillParam from .response_item_list import ResponseItemList as ResponseItemList from .tool_choice_custom import ToolChoiceCustom as ToolChoiceCustom from .computer_tool_param import ComputerToolParam as ComputerToolParam +from .container_reference import ContainerReference as ContainerReference from .function_shell_tool import FunctionShellTool as FunctionShellTool from .function_tool_param import FunctionToolParam as FunctionToolParam +from .inline_skill_source import InlineSkillSource as InlineSkillSource from .response_includable import ResponseIncludable as ResponseIncludable from .response_input_file import ResponseInputFile as ResponseInputFile from .response_input_item import ResponseInputItem as ResponseInputItem from .response_input_text import ResponseInputText as ResponseInputText from .tool_choice_allowed import ToolChoiceAllowed as ToolChoiceAllowed from .tool_choice_options import ToolChoiceOptions as ToolChoiceOptions +from .container_auto_param import ContainerAutoParam as ContainerAutoParam from .response_error_event import ResponseErrorEvent as ResponseErrorEvent from .response_input_audio import ResponseInputAudio as ResponseInputAudio from .response_input_image import ResponseInputImage as ResponseInputImage @@ -53,6 +63,7 @@ from .response_prompt_param import ResponsePromptParam as ResponsePromptParam from .response_queued_event import ResponseQueuedEvent as ResponseQueuedEvent from .response_stream_event import ResponseStreamEvent as ResponseStreamEvent +from .skill_reference_param import SkillReferenceParam as SkillReferenceParam from .tool_choice_mcp_param import ToolChoiceMcpParam as ToolChoiceMcpParam from .web_search_tool_param import WebSearchToolParam as WebSearchToolParam from .apply_patch_tool_param import ApplyPatchToolParam as ApplyPatchToolParam @@ -61,6 +72,7 @@ from .response_create_params import ResponseCreateParams as ResponseCreateParams from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent from .response_input_content import ResponseInputContent as ResponseInputContent +from .local_environment_param import LocalEnvironmentParam as LocalEnvironmentParam from .response_compact_params import ResponseCompactParams as ResponseCompactParams from .response_output_message import ResponseOutputMessage as ResponseOutputMessage from .response_output_refusal import ResponseOutputRefusal as ResponseOutputRefusal @@ -76,7 +88,9 @@ from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent from .tool_choice_custom_param import ToolChoiceCustomParam as ToolChoiceCustomParam +from .container_reference_param import ContainerReferenceParam as ContainerReferenceParam from .function_shell_tool_param import FunctionShellToolParam as FunctionShellToolParam +from .inline_skill_source_param import InlineSkillSourceParam as InlineSkillSourceParam from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent from .response_custom_tool_call import ResponseCustomToolCall as ResponseCustomToolCall from .response_incomplete_event import ResponseIncompleteEvent as ResponseIncompleteEvent @@ -90,6 +104,7 @@ from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent from .response_input_audio_param import ResponseInputAudioParam as ResponseInputAudioParam from .response_input_image_param import ResponseInputImageParam as ResponseInputImageParam +from .response_local_environment import ResponseLocalEnvironment as ResponseLocalEnvironment from .response_output_text_param import ResponseOutputTextParam as ResponseOutputTextParam from .response_text_config_param import ResponseTextConfigParam as ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam as ToolChoiceFunctionParam @@ -101,6 +116,7 @@ from .response_input_message_item import ResponseInputMessageItem as ResponseInputMessageItem from .response_input_text_content import ResponseInputTextContent as ResponseInputTextContent from .response_refusal_done_event import ResponseRefusalDoneEvent as ResponseRefusalDoneEvent +from .response_container_reference import ResponseContainerReference as ResponseContainerReference from .response_function_web_search import ResponseFunctionWebSearch as ResponseFunctionWebSearch from .response_input_content_param import ResponseInputContentParam as ResponseInputContentParam from .response_input_image_content import ResponseInputImageContent as ResponseInputImageContent @@ -120,6 +136,7 @@ from .response_custom_tool_call_output import ResponseCustomToolCallOutput as ResponseCustomToolCallOutput from .response_function_tool_call_item import ResponseFunctionToolCallItem as ResponseFunctionToolCallItem from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .container_network_policy_disabled import ContainerNetworkPolicyDisabled as ContainerNetworkPolicyDisabled from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent from .response_format_text_config_param import ResponseFormatTextConfigParam as ResponseFormatTextConfigParam @@ -128,6 +145,7 @@ from .response_input_file_content_param import ResponseInputFileContentParam as ResponseInputFileContentParam from .response_input_text_content_param import ResponseInputTextContentParam as ResponseInputTextContentParam from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent as ResponseMcpCallCompletedEvent +from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist as ContainerNetworkPolicyAllowlist from .response_function_call_output_item import ResponseFunctionCallOutputItem as ResponseFunctionCallOutputItem from .response_function_web_search_param import ResponseFunctionWebSearchParam as ResponseFunctionWebSearchParam from .response_input_image_content_param import ResponseInputImageContentParam as ResponseInputImageContentParam @@ -144,12 +162,18 @@ from .response_audio_transcript_delta_event import ( ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, ) +from .container_network_policy_domain_secret import ( + ContainerNetworkPolicyDomainSecret as ContainerNetworkPolicyDomainSecret, +) from .response_custom_tool_call_output_param import ( ResponseCustomToolCallOutputParam as ResponseCustomToolCallOutputParam, ) from .response_mcp_call_arguments_done_event import ( ResponseMcpCallArgumentsDoneEvent as ResponseMcpCallArgumentsDoneEvent, ) +from .container_network_policy_disabled_param import ( + ContainerNetworkPolicyDisabledParam as ContainerNetworkPolicyDisabledParam, +) from .response_computer_tool_call_output_item import ( ResponseComputerToolCallOutputItem as ResponseComputerToolCallOutputItem, ) @@ -171,6 +195,9 @@ from .response_mcp_list_tools_completed_event import ( ResponseMcpListToolsCompletedEvent as ResponseMcpListToolsCompletedEvent, ) +from .container_network_policy_allowlist_param import ( + ContainerNetworkPolicyAllowlistParam as ContainerNetworkPolicyAllowlistParam, +) from .response_function_call_output_item_param import ( ResponseFunctionCallOutputItemParam as ResponseFunctionCallOutputItemParam, ) @@ -240,6 +267,9 @@ from .response_reasoning_summary_text_delta_event import ( ResponseReasoningSummaryTextDeltaEvent as ResponseReasoningSummaryTextDeltaEvent, ) +from .container_network_policy_domain_secret_param import ( + ContainerNetworkPolicyDomainSecretParam as ContainerNetworkPolicyDomainSecretParam, +) from .response_function_call_arguments_delta_event import ( ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, ) diff --git a/src/openai/types/responses/container_auto.py b/src/openai/types/responses/container_auto.py new file mode 100644 index 0000000000..06b8a285bf --- /dev/null +++ b/src/openai/types/responses/container_auto.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .inline_skill import InlineSkill +from .skill_reference import SkillReference +from .container_network_policy_disabled import ContainerNetworkPolicyDisabled +from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist + +__all__ = ["ContainerAuto", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Annotated[ + Union[ContainerNetworkPolicyDisabled, ContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + +Skill: TypeAlias = Annotated[Union[SkillReference, InlineSkill], PropertyInfo(discriminator="type")] + + +class ContainerAuto(BaseModel): + type: Literal["container_auto"] + """Automatically creates a container for this request""" + + file_ids: Optional[List[str]] = None + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" + + skills: Optional[List[Skill]] = None + """An optional list of skills referenced by id or inline data.""" diff --git a/src/openai/types/responses/container_auto_param.py b/src/openai/types/responses/container_auto_param.py new file mode 100644 index 0000000000..b9e8bb5223 --- /dev/null +++ b/src/openai/types/responses/container_auto_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .inline_skill_param import InlineSkillParam +from .skill_reference_param import SkillReferenceParam +from .container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam +from .container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam + +__all__ = ["ContainerAutoParam", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Union[ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam] + +Skill: TypeAlias = Union[SkillReferenceParam, InlineSkillParam] + + +class ContainerAutoParam(TypedDict, total=False): + type: Required[Literal["container_auto"]] + """Automatically creates a container for this request""" + + file_ids: SequenceNotStr[str] + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the container.""" + + network_policy: NetworkPolicy + """Network access policy for the container.""" + + skills: Iterable[Skill] + """An optional list of skills referenced by id or inline data.""" diff --git a/src/openai/types/responses/container_network_policy_allowlist.py b/src/openai/types/responses/container_network_policy_allowlist.py new file mode 100644 index 0000000000..caa2565ea2 --- /dev/null +++ b/src/openai/types/responses/container_network_policy_allowlist.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .container_network_policy_domain_secret import ContainerNetworkPolicyDomainSecret + +__all__ = ["ContainerNetworkPolicyAllowlist"] + + +class ContainerNetworkPolicyAllowlist(BaseModel): + allowed_domains: List[str] + """A list of allowed domains when type is `allowlist`.""" + + type: Literal["allowlist"] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Optional[List[ContainerNetworkPolicyDomainSecret]] = None + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/src/openai/types/responses/container_network_policy_allowlist_param.py b/src/openai/types/responses/container_network_policy_allowlist_param.py new file mode 100644 index 0000000000..583b761e69 --- /dev/null +++ b/src/openai/types/responses/container_network_policy_allowlist_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr +from .container_network_policy_domain_secret_param import ContainerNetworkPolicyDomainSecretParam + +__all__ = ["ContainerNetworkPolicyAllowlistParam"] + + +class ContainerNetworkPolicyAllowlistParam(TypedDict, total=False): + allowed_domains: Required[SequenceNotStr[str]] + """A list of allowed domains when type is `allowlist`.""" + + type: Required[Literal["allowlist"]] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Iterable[ContainerNetworkPolicyDomainSecretParam] + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/src/openai/types/responses/container_network_policy_disabled.py b/src/openai/types/responses/container_network_policy_disabled.py new file mode 100644 index 0000000000..47891aa338 --- /dev/null +++ b/src/openai/types/responses/container_network_policy_disabled.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ContainerNetworkPolicyDisabled"] + + +class ContainerNetworkPolicyDisabled(BaseModel): + type: Literal["disabled"] + """Disable outbound network access. Always `disabled`.""" diff --git a/src/openai/types/responses/container_network_policy_disabled_param.py b/src/openai/types/responses/container_network_policy_disabled_param.py new file mode 100644 index 0000000000..0db7d8ff79 --- /dev/null +++ b/src/openai/types/responses/container_network_policy_disabled_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ContainerNetworkPolicyDisabledParam"] + + +class ContainerNetworkPolicyDisabledParam(TypedDict, total=False): + type: Required[Literal["disabled"]] + """Disable outbound network access. Always `disabled`.""" diff --git a/src/openai/types/responses/container_network_policy_domain_secret.py b/src/openai/types/responses/container_network_policy_domain_secret.py new file mode 100644 index 0000000000..d0e18ba977 --- /dev/null +++ b/src/openai/types/responses/container_network_policy_domain_secret.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["ContainerNetworkPolicyDomainSecret"] + + +class ContainerNetworkPolicyDomainSecret(BaseModel): + domain: str + """The domain associated with the secret.""" + + name: str + """The name of the secret to inject for the domain.""" + + value: str + """The secret value to inject for the domain.""" diff --git a/src/openai/types/responses/container_network_policy_domain_secret_param.py b/src/openai/types/responses/container_network_policy_domain_secret_param.py new file mode 100644 index 0000000000..619fbde7a2 --- /dev/null +++ b/src/openai/types/responses/container_network_policy_domain_secret_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ContainerNetworkPolicyDomainSecretParam"] + + +class ContainerNetworkPolicyDomainSecretParam(TypedDict, total=False): + domain: Required[str] + """The domain associated with the secret.""" + + name: Required[str] + """The name of the secret to inject for the domain.""" + + value: Required[str] + """The secret value to inject for the domain.""" diff --git a/src/openai/types/responses/container_reference.py b/src/openai/types/responses/container_reference.py new file mode 100644 index 0000000000..17065bf30a --- /dev/null +++ b/src/openai/types/responses/container_reference.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ContainerReference"] + + +class ContainerReference(BaseModel): + container_id: str + """The ID of the referenced container.""" + + type: Literal["container_reference"] + """References a container created with the /v1/containers endpoint""" diff --git a/src/openai/types/responses/container_reference_param.py b/src/openai/types/responses/container_reference_param.py new file mode 100644 index 0000000000..1da2fb6763 --- /dev/null +++ b/src/openai/types/responses/container_reference_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ContainerReferenceParam"] + + +class ContainerReferenceParam(TypedDict, total=False): + container_id: Required[str] + """The ID of the referenced container.""" + + type: Required[Literal["container_reference"]] + """References a container created with the /v1/containers endpoint""" diff --git a/src/openai/types/responses/function_shell_tool.py b/src/openai/types/responses/function_shell_tool.py index 5b237aa705..17d6bb36c9 100644 --- a/src/openai/types/responses/function_shell_tool.py +++ b/src/openai/types/responses/function_shell_tool.py @@ -1,10 +1,19 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel +from .container_auto import ContainerAuto +from .local_environment import LocalEnvironment +from .container_reference import ContainerReference -__all__ = ["FunctionShellTool"] +__all__ = ["FunctionShellTool", "Environment"] + +Environment: TypeAlias = Annotated[ + Union[ContainerAuto, LocalEnvironment, ContainerReference, None], PropertyInfo(discriminator="type") +] class FunctionShellTool(BaseModel): @@ -12,3 +21,5 @@ class FunctionShellTool(BaseModel): type: Literal["shell"] """The type of the shell tool. Always `shell`.""" + + environment: Optional[Environment] = None diff --git a/src/openai/types/responses/function_shell_tool_param.py b/src/openai/types/responses/function_shell_tool_param.py index c640ddab99..b8464ed341 100644 --- a/src/openai/types/responses/function_shell_tool_param.py +++ b/src/openai/types/responses/function_shell_tool_param.py @@ -2,9 +2,16 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["FunctionShellToolParam"] +from .container_auto_param import ContainerAutoParam +from .local_environment_param import LocalEnvironmentParam +from .container_reference_param import ContainerReferenceParam + +__all__ = ["FunctionShellToolParam", "Environment"] + +Environment: TypeAlias = Union[ContainerAutoParam, LocalEnvironmentParam, ContainerReferenceParam] class FunctionShellToolParam(TypedDict, total=False): @@ -12,3 +19,5 @@ class FunctionShellToolParam(TypedDict, total=False): type: Required[Literal["shell"]] """The type of the shell tool. Always `shell`.""" + + environment: Optional[Environment] diff --git a/src/openai/types/responses/inline_skill.py b/src/openai/types/responses/inline_skill.py new file mode 100644 index 0000000000..87c7ca5f7a --- /dev/null +++ b/src/openai/types/responses/inline_skill.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .inline_skill_source import InlineSkillSource + +__all__ = ["InlineSkill"] + + +class InlineSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + source: InlineSkillSource + """Inline skill payload""" + + type: Literal["inline"] + """Defines an inline skill for this request.""" diff --git a/src/openai/types/responses/inline_skill_param.py b/src/openai/types/responses/inline_skill_param.py new file mode 100644 index 0000000000..f9181693be --- /dev/null +++ b/src/openai/types/responses/inline_skill_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .inline_skill_source_param import InlineSkillSourceParam + +__all__ = ["InlineSkillParam"] + + +class InlineSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + source: Required[InlineSkillSourceParam] + """Inline skill payload""" + + type: Required[Literal["inline"]] + """Defines an inline skill for this request.""" diff --git a/src/openai/types/responses/inline_skill_source.py b/src/openai/types/responses/inline_skill_source.py new file mode 100644 index 0000000000..2586cdb008 --- /dev/null +++ b/src/openai/types/responses/inline_skill_source.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["InlineSkillSource"] + + +class InlineSkillSource(BaseModel): + """Inline skill payload""" + + data: str + """Base64-encoded skill zip bundle.""" + + media_type: Literal["application/zip"] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Literal["base64"] + """The type of the inline skill source. Must be `base64`.""" diff --git a/src/openai/types/responses/inline_skill_source_param.py b/src/openai/types/responses/inline_skill_source_param.py new file mode 100644 index 0000000000..b14e63e2af --- /dev/null +++ b/src/openai/types/responses/inline_skill_source_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InlineSkillSourceParam"] + + +class InlineSkillSourceParam(TypedDict, total=False): + """Inline skill payload""" + + data: Required[str] + """Base64-encoded skill zip bundle.""" + + media_type: Required[Literal["application/zip"]] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Required[Literal["base64"]] + """The type of the inline skill source. Must be `base64`.""" diff --git a/src/openai/types/responses/local_environment.py b/src/openai/types/responses/local_environment.py new file mode 100644 index 0000000000..3ebdaa7e2a --- /dev/null +++ b/src/openai/types/responses/local_environment.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .local_skill import LocalSkill + +__all__ = ["LocalEnvironment"] + + +class LocalEnvironment(BaseModel): + type: Literal["local"] + """Use a local computer environment.""" + + skills: Optional[List[LocalSkill]] = None + """An optional list of skills.""" diff --git a/src/openai/types/responses/local_environment_param.py b/src/openai/types/responses/local_environment_param.py new file mode 100644 index 0000000000..0ed1e81ffc --- /dev/null +++ b/src/openai/types/responses/local_environment_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .local_skill_param import LocalSkillParam + +__all__ = ["LocalEnvironmentParam"] + + +class LocalEnvironmentParam(TypedDict, total=False): + type: Required[Literal["local"]] + """Use a local computer environment.""" + + skills: Iterable[LocalSkillParam] + """An optional list of skills.""" diff --git a/src/openai/types/responses/local_skill.py b/src/openai/types/responses/local_skill.py new file mode 100644 index 0000000000..1033c319d4 --- /dev/null +++ b/src/openai/types/responses/local_skill.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["LocalSkill"] + + +class LocalSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + path: str + """The path to the directory containing the skill.""" diff --git a/src/openai/types/responses/local_skill_param.py b/src/openai/types/responses/local_skill_param.py new file mode 100644 index 0000000000..63011a49aa --- /dev/null +++ b/src/openai/types/responses/local_skill_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["LocalSkillParam"] + + +class LocalSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + path: Required[str] + """The path to the directory containing the skill.""" diff --git a/src/openai/types/responses/response_container_reference.py b/src/openai/types/responses/response_container_reference.py new file mode 100644 index 0000000000..81f5bd8dad --- /dev/null +++ b/src/openai/types/responses/response_container_reference.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseContainerReference"] + + +class ResponseContainerReference(BaseModel): + """Represents a container created with /v1/containers.""" + + container_id: str + + type: Literal["container_reference"] + """The environment type. Always `container_reference`.""" diff --git a/src/openai/types/responses/response_function_shell_tool_call.py b/src/openai/types/responses/response_function_shell_tool_call.py index 7c6a184ed4..22c75cf043 100644 --- a/src/openai/types/responses/response_function_shell_tool_call.py +++ b/src/openai/types/responses/response_function_shell_tool_call.py @@ -1,11 +1,14 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional -from typing_extensions import Literal +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel +from .response_local_environment import ResponseLocalEnvironment +from .response_container_reference import ResponseContainerReference -__all__ = ["ResponseFunctionShellToolCall", "Action"] +__all__ = ["ResponseFunctionShellToolCall", "Action", "Environment"] class Action(BaseModel): @@ -20,6 +23,11 @@ class Action(BaseModel): """Optional timeout in milliseconds for the commands.""" +Environment: TypeAlias = Annotated[ + Union[ResponseLocalEnvironment, ResponseContainerReference, None], PropertyInfo(discriminator="type") +] + + class ResponseFunctionShellToolCall(BaseModel): """A tool call that executes one or more shell commands in a managed environment.""" @@ -35,6 +43,9 @@ class ResponseFunctionShellToolCall(BaseModel): call_id: str """The unique ID of the shell tool call generated by the model.""" + environment: Optional[Environment] = None + """Represents the use of a local environment to perform shell actions.""" + status: Literal["in_progress", "completed", "incomplete"] """The status of the shell call. diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index e1f521f957..2b664fd875 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -5,7 +5,9 @@ from ..._utils import PropertyInfo from ..._models import BaseModel +from .local_environment import LocalEnvironment from .easy_input_message import EasyInputMessage +from .container_reference import ContainerReference from .response_output_message import ResponseOutputMessage from .response_reasoning_item import ResponseReasoningItem from .response_custom_tool_call import ResponseCustomToolCall @@ -33,6 +35,7 @@ "LocalShellCallOutput", "ShellCall", "ShellCallAction", + "ShellCallEnvironment", "ShellCallOutput", "ApplyPatchCall", "ApplyPatchCallOperation", @@ -233,6 +236,11 @@ class ShellCallAction(BaseModel): """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" +ShellCallEnvironment: TypeAlias = Annotated[ + Union[LocalEnvironment, ContainerReference, None], PropertyInfo(discriminator="type") +] + + class ShellCall(BaseModel): """A tool representing a request to execute one or more shell commands.""" @@ -251,6 +259,9 @@ class ShellCall(BaseModel): Populated when this item is returned via API. """ + environment: Optional[ShellCallEnvironment] = None + """The environment to execute the shell commands in.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None """The status of the shell call. diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 03fe86c42c..f52a7495db 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -6,7 +6,9 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .local_environment_param import LocalEnvironmentParam from .easy_input_message_param import EasyInputMessageParam +from .container_reference_param import ContainerReferenceParam from .response_output_message_param import ResponseOutputMessageParam from .response_reasoning_item_param import ResponseReasoningItemParam from .response_custom_tool_call_param import ResponseCustomToolCallParam @@ -34,6 +36,7 @@ "LocalShellCallOutput", "ShellCall", "ShellCallAction", + "ShellCallEnvironment", "ShellCallOutput", "ApplyPatchCall", "ApplyPatchCallOperation", @@ -234,6 +237,9 @@ class ShellCallAction(TypedDict, total=False): """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" +ShellCallEnvironment: TypeAlias = Union[LocalEnvironmentParam, ContainerReferenceParam] + + class ShellCall(TypedDict, total=False): """A tool representing a request to execute one or more shell commands.""" @@ -252,6 +258,9 @@ class ShellCall(TypedDict, total=False): Populated when this item is returned via API. """ + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the shell call. diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 5be65b3e14..c125d03f84 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -6,7 +6,9 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .local_environment_param import LocalEnvironmentParam from .easy_input_message_param import EasyInputMessageParam +from .container_reference_param import ContainerReferenceParam from .response_output_message_param import ResponseOutputMessageParam from .response_reasoning_item_param import ResponseReasoningItemParam from .response_custom_tool_call_param import ResponseCustomToolCallParam @@ -35,6 +37,7 @@ "LocalShellCallOutput", "ShellCall", "ShellCallAction", + "ShellCallEnvironment", "ShellCallOutput", "ApplyPatchCall", "ApplyPatchCallOperation", @@ -235,6 +238,9 @@ class ShellCallAction(TypedDict, total=False): """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" +ShellCallEnvironment: TypeAlias = Union[LocalEnvironmentParam, ContainerReferenceParam] + + class ShellCall(TypedDict, total=False): """A tool representing a request to execute one or more shell commands.""" @@ -253,6 +259,9 @@ class ShellCall(TypedDict, total=False): Populated when this item is returned via API. """ + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the shell call. diff --git a/src/openai/types/responses/response_local_environment.py b/src/openai/types/responses/response_local_environment.py new file mode 100644 index 0000000000..6467fcb4c5 --- /dev/null +++ b/src/openai/types/responses/response_local_environment.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseLocalEnvironment"] + + +class ResponseLocalEnvironment(BaseModel): + """Represents the use of a local environment to perform shell actions.""" + + type: Literal["local"] + """The environment type. Always `local`.""" diff --git a/src/openai/types/responses/skill_reference.py b/src/openai/types/responses/skill_reference.py new file mode 100644 index 0000000000..76e614ab6c --- /dev/null +++ b/src/openai/types/responses/skill_reference.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SkillReference"] + + +class SkillReference(BaseModel): + skill_id: str + """The ID of the referenced skill.""" + + type: Literal["skill_reference"] + """References a skill created with the /v1/skills endpoint.""" + + version: Optional[str] = None + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/src/openai/types/responses/skill_reference_param.py b/src/openai/types/responses/skill_reference_param.py new file mode 100644 index 0000000000..33b572854d --- /dev/null +++ b/src/openai/types/responses/skill_reference_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SkillReferenceParam"] + + +class SkillReferenceParam(TypedDict, total=False): + skill_id: Required[str] + """The ID of the referenced skill.""" + + type: Required[Literal["skill_reference"]] + """References a skill created with the /v1/skills endpoint.""" + + version: str + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index bd266ea753..a98b4ca758 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -14,6 +14,8 @@ from .file_search_tool import FileSearchTool from .function_shell_tool import FunctionShellTool from .web_search_preview_tool import WebSearchPreviewTool +from .container_network_policy_disabled import ContainerNetworkPolicyDisabled +from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist __all__ = [ "Tool", @@ -28,6 +30,7 @@ "CodeInterpreter", "CodeInterpreterContainer", "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", "ImageGeneration", "ImageGenerationInputImageMask", "LocalShell", @@ -174,6 +177,11 @@ class Mcp(BaseModel): """ +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Annotated[ + Union[ContainerNetworkPolicyDisabled, ContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + + class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): """Configuration for a code interpreter container. @@ -189,6 +197,9 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None """The memory limit for the code interpreter container.""" + network_policy: Optional[CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy] = None + """Network access policy for the container.""" + CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index a81c69d3ab..a351065dea 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -16,6 +16,8 @@ from .file_search_tool_param import FileSearchToolParam from .function_shell_tool_param import FunctionShellToolParam from .web_search_preview_tool_param import WebSearchPreviewToolParam +from .container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam +from .container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam __all__ = [ "ToolParam", @@ -29,6 +31,7 @@ "CodeInterpreter", "CodeInterpreterContainer", "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", "ImageGeneration", "ImageGenerationInputImageMask", "LocalShell", @@ -174,6 +177,11 @@ class Mcp(TypedDict, total=False): """ +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Union[ + ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam +] + + class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): """Configuration for a code interpreter container. @@ -189,6 +197,9 @@ class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] """The memory limit for the code interpreter container.""" + network_policy: CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy + """Network access policy for the container.""" + CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] diff --git a/src/openai/types/skill.py b/src/openai/types/skill.py new file mode 100644 index 0000000000..25d74978d8 --- /dev/null +++ b/src/openai/types/skill.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["Skill"] + + +class Skill(BaseModel): + id: str + """Unique identifier for the skill.""" + + created_at: int + """Unix timestamp (seconds) for when the skill was created.""" + + default_version: str + """Default version for the skill.""" + + description: str + """Description of the skill.""" + + latest_version: str + """Latest version for the skill.""" + + name: str + """Name of the skill.""" + + object: Literal["skill"] + """The object type, which is `skill`.""" diff --git a/src/openai/types/skill_create_params.py b/src/openai/types/skill_create_params.py new file mode 100644 index 0000000000..5a709286a1 --- /dev/null +++ b/src/openai/types/skill_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypedDict + +from .._types import FileTypes, SequenceNotStr + +__all__ = ["SkillCreateParams"] + + +class SkillCreateParams(TypedDict, total=False): + files: Union[SequenceNotStr[FileTypes], FileTypes] + """Skill files to upload (directory upload) or a single zip file.""" diff --git a/src/openai/types/skill_list.py b/src/openai/types/skill_list.py new file mode 100644 index 0000000000..fc5b57bc26 --- /dev/null +++ b/src/openai/types/skill_list.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .skill import Skill +from .._models import BaseModel + +__all__ = ["SkillList"] + + +class SkillList(BaseModel): + data: List[Skill] + """A list of items""" + + first_id: Optional[str] = None + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: Optional[str] = None + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/src/openai/types/skill_list_params.py b/src/openai/types/skill_list_params.py new file mode 100644 index 0000000000..ffe7967915 --- /dev/null +++ b/src/openai/types/skill_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SkillListParams"] + + +class SkillListParams(TypedDict, total=False): + after: str + """Identifier for the last item from the previous pagination request""" + + limit: int + """Number of items to retrieve""" + + order: Literal["asc", "desc"] + """Sort order of results by timestamp. + + Use `asc` for ascending order or `desc` for descending order. + """ diff --git a/src/openai/types/skill_update_params.py b/src/openai/types/skill_update_params.py new file mode 100644 index 0000000000..48a790e1fc --- /dev/null +++ b/src/openai/types/skill_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SkillUpdateParams"] + + +class SkillUpdateParams(TypedDict, total=False): + default_version: Required[str] + """The skill version number to set as default.""" diff --git a/src/openai/types/skills/__init__.py b/src/openai/types/skills/__init__.py new file mode 100644 index 0000000000..b0605fb85b --- /dev/null +++ b/src/openai/types/skills/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .skill_version import SkillVersion as SkillVersion +from .skill_version_list import SkillVersionList as SkillVersionList +from .version_list_params import VersionListParams as VersionListParams +from .deleted_skill_version import DeletedSkillVersion as DeletedSkillVersion +from .version_create_params import VersionCreateParams as VersionCreateParams diff --git a/src/openai/types/skills/deleted_skill_version.py b/src/openai/types/skills/deleted_skill_version.py new file mode 100644 index 0000000000..e1fd8c4985 --- /dev/null +++ b/src/openai/types/skills/deleted_skill_version.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DeletedSkillVersion"] + + +class DeletedSkillVersion(BaseModel): + id: str + + deleted: bool + + object: Literal["skill.version.deleted"] + + version: str + """The deleted skill version.""" diff --git a/src/openai/types/skills/skill_version.py b/src/openai/types/skills/skill_version.py new file mode 100644 index 0000000000..74b47000a5 --- /dev/null +++ b/src/openai/types/skills/skill_version.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SkillVersion"] + + +class SkillVersion(BaseModel): + id: str + """Unique identifier for the skill version.""" + + created_at: int + """Unix timestamp (seconds) for when the version was created.""" + + description: str + """Description of the skill version.""" + + name: str + """Name of the skill version.""" + + object: Literal["skill.version"] + """The object type, which is `skill.version`.""" + + skill_id: str + """Identifier of the skill for this version.""" + + version: str + """Version number for this skill.""" diff --git a/src/openai/types/skills/skill_version_list.py b/src/openai/types/skills/skill_version_list.py new file mode 100644 index 0000000000..7d10082f1c --- /dev/null +++ b/src/openai/types/skills/skill_version_list.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .skill_version import SkillVersion + +__all__ = ["SkillVersionList"] + + +class SkillVersionList(BaseModel): + data: List[SkillVersion] + """A list of items""" + + first_id: Optional[str] = None + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: Optional[str] = None + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/src/openai/types/skills/version_create_params.py b/src/openai/types/skills/version_create_params.py new file mode 100644 index 0000000000..043b43a030 --- /dev/null +++ b/src/openai/types/skills/version_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypedDict + +from ..._types import FileTypes, SequenceNotStr + +__all__ = ["VersionCreateParams"] + + +class VersionCreateParams(TypedDict, total=False): + default: bool + """Whether to set this version as the default.""" + + files: Union[SequenceNotStr[FileTypes], FileTypes] + """Skill files to upload (directory upload) or a single zip file.""" diff --git a/src/openai/types/skills/version_list_params.py b/src/openai/types/skills/version_list_params.py new file mode 100644 index 0000000000..0638f40086 --- /dev/null +++ b/src/openai/types/skills/version_list_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["VersionListParams"] + + +class VersionListParams(TypedDict, total=False): + after: str + """The skill version ID to start after.""" + + limit: int + """Number of versions to retrieve.""" + + order: Literal["asc", "desc"] + """Sort order of results by version number.""" diff --git a/src/openai/types/skills/versions/__init__.py b/src/openai/types/skills/versions/__init__.py new file mode 100644 index 0000000000..f8ee8b14b1 --- /dev/null +++ b/src/openai/types/skills/versions/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/tests/api_resources/skills/__init__.py b/tests/api_resources/skills/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/skills/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/skills/test_content.py b/tests/api_resources/skills/test_content.py new file mode 100644 index 0000000000..f93e6fc015 --- /dev/null +++ b/tests/api_resources/skills/test_content.py @@ -0,0 +1,122 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import httpx +import pytest +from respx import MockRouter + +import openai._legacy_response as _legacy_response +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type + +# pyright: reportDeprecated=false + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestContent: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + content = client.skills.content.retrieve( + "skill_123", + ) + assert isinstance(content, _legacy_response.HttpxBinaryResponseContent) + assert content.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.skills.content.with_raw_response.retrieve( + "skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + with client.skills.content.with_streaming_response.retrieve( + "skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + content = response.parse() + assert_matches_type(bytes, content, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.content.with_raw_response.retrieve( + "", + ) + + +class TestAsyncContent: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + content = await async_client.skills.content.retrieve( + "skill_123", + ) + assert isinstance(content, _legacy_response.HttpxBinaryResponseContent) + assert content.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.skills.content.with_raw_response.retrieve( + "skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/content").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + async with async_client.skills.content.with_streaming_response.retrieve( + "skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + content = await response.parse() + assert_matches_type(bytes, content, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.content.with_raw_response.retrieve( + "", + ) diff --git a/tests/api_resources/skills/test_versions.py b/tests/api_resources/skills/test_versions.py new file mode 100644 index 0000000000..5f4dcbf51d --- /dev/null +++ b/tests/api_resources/skills/test_versions.py @@ -0,0 +1,407 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.skills import SkillVersion, DeletedSkillVersion + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestVersions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + version = client.skills.versions.create( + skill_id="skill_123", + ) + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + version = client.skills.versions.create( + skill_id="skill_123", + default=True, + files=[b"raw file contents"], + ) + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.skills.versions.with_raw_response.create( + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.skills.versions.with_streaming_response.create( + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.versions.with_raw_response.create( + skill_id="", + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + version = client.skills.versions.retrieve( + version="version", + skill_id="skill_123", + ) + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.skills.versions.with_raw_response.retrieve( + version="version", + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.skills.versions.with_streaming_response.retrieve( + version="version", + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.versions.with_raw_response.retrieve( + version="version", + skill_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): + client.skills.versions.with_raw_response.retrieve( + version="", + skill_id="skill_123", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + version = client.skills.versions.list( + skill_id="skill_123", + ) + assert_matches_type(SyncCursorPage[SkillVersion], version, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + version = client.skills.versions.list( + skill_id="skill_123", + after="skillver_123", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[SkillVersion], version, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.skills.versions.with_raw_response.list( + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(SyncCursorPage[SkillVersion], version, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.skills.versions.with_streaming_response.list( + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = response.parse() + assert_matches_type(SyncCursorPage[SkillVersion], version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.versions.with_raw_response.list( + skill_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + version = client.skills.versions.delete( + version="version", + skill_id="skill_123", + ) + assert_matches_type(DeletedSkillVersion, version, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.skills.versions.with_raw_response.delete( + version="version", + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(DeletedSkillVersion, version, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.skills.versions.with_streaming_response.delete( + version="version", + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = response.parse() + assert_matches_type(DeletedSkillVersion, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.versions.with_raw_response.delete( + version="version", + skill_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): + client.skills.versions.with_raw_response.delete( + version="", + skill_id="skill_123", + ) + + +class TestAsyncVersions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + version = await async_client.skills.versions.create( + skill_id="skill_123", + ) + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + version = await async_client.skills.versions.create( + skill_id="skill_123", + default=True, + files=[b"raw file contents"], + ) + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.versions.with_raw_response.create( + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.versions.with_streaming_response.create( + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = await response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.versions.with_raw_response.create( + skill_id="", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + version = await async_client.skills.versions.retrieve( + version="version", + skill_id="skill_123", + ) + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.versions.with_raw_response.retrieve( + version="version", + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.versions.with_streaming_response.retrieve( + version="version", + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = await response.parse() + assert_matches_type(SkillVersion, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.versions.with_raw_response.retrieve( + version="version", + skill_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): + await async_client.skills.versions.with_raw_response.retrieve( + version="", + skill_id="skill_123", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + version = await async_client.skills.versions.list( + skill_id="skill_123", + ) + assert_matches_type(AsyncCursorPage[SkillVersion], version, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + version = await async_client.skills.versions.list( + skill_id="skill_123", + after="skillver_123", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[SkillVersion], version, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.versions.with_raw_response.list( + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(AsyncCursorPage[SkillVersion], version, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.versions.with_streaming_response.list( + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = await response.parse() + assert_matches_type(AsyncCursorPage[SkillVersion], version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.versions.with_raw_response.list( + skill_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + version = await async_client.skills.versions.delete( + version="version", + skill_id="skill_123", + ) + assert_matches_type(DeletedSkillVersion, version, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.versions.with_raw_response.delete( + version="version", + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + version = response.parse() + assert_matches_type(DeletedSkillVersion, version, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.versions.with_streaming_response.delete( + version="version", + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + version = await response.parse() + assert_matches_type(DeletedSkillVersion, version, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.versions.with_raw_response.delete( + version="version", + skill_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): + await async_client.skills.versions.with_raw_response.delete( + version="", + skill_id="skill_123", + ) diff --git a/tests/api_resources/skills/versions/__init__.py b/tests/api_resources/skills/versions/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/skills/versions/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/skills/versions/test_content.py b/tests/api_resources/skills/versions/test_content.py new file mode 100644 index 0000000000..3d3e19519e --- /dev/null +++ b/tests/api_resources/skills/versions/test_content.py @@ -0,0 +1,154 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import httpx +import pytest +from respx import MockRouter + +import openai._legacy_response as _legacy_response +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type + +# pyright: reportDeprecated=false + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestContent: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/versions/version/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + content = client.skills.versions.content.retrieve( + version="version", + skill_id="skill_123", + ) + assert isinstance(content, _legacy_response.HttpxBinaryResponseContent) + assert content.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/versions/version/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + response = client.skills.versions.content.with_raw_response.retrieve( + version="version", + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_retrieve(self, client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/versions/version/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + with client.skills.versions.content.with_streaming_response.retrieve( + version="version", + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + content = response.parse() + assert_matches_type(bytes, content, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.versions.content.with_raw_response.retrieve( + version="version", + skill_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): + client.skills.versions.content.with_raw_response.retrieve( + version="", + skill_id="skill_123", + ) + + +class TestAsyncContent: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/versions/version/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + content = await async_client.skills.versions.content.retrieve( + version="version", + skill_id="skill_123", + ) + assert isinstance(content, _legacy_response.HttpxBinaryResponseContent) + assert content.json() == {"foo": "bar"} + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/versions/version/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + response = await async_client.skills.versions.content.with_raw_response.retrieve( + version="version", + skill_id="skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = response.parse() + assert_matches_type(_legacy_response.HttpxBinaryResponseContent, content, path=["response"]) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/skills/skill_123/versions/version/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + async with async_client.skills.versions.content.with_streaming_response.retrieve( + version="version", + skill_id="skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + content = await response.parse() + assert_matches_type(bytes, content, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.versions.content.with_raw_response.retrieve( + version="version", + skill_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): + await async_client.skills.versions.content.with_raw_response.retrieve( + version="", + skill_id="skill_123", + ) diff --git a/tests/api_resources/test_skills.py b/tests/api_resources/test_skills.py new file mode 100644 index 0000000000..fb4cea92ce --- /dev/null +++ b/tests/api_resources/test_skills.py @@ -0,0 +1,393 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types import Skill, DeletedSkill +from openai.pagination import SyncCursorPage, AsyncCursorPage + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSkills: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + skill = client.skills.create() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + skill = client.skills.create( + files=[b"raw file contents"], + ) + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.skills.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.skills.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + skill = client.skills.retrieve( + "skill_123", + ) + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.skills.with_raw_response.retrieve( + "skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.skills.with_streaming_response.retrieve( + "skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + skill = client.skills.update( + skill_id="skill_123", + default_version="default_version", + ) + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.skills.with_raw_response.update( + skill_id="skill_123", + default_version="default_version", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.skills.with_streaming_response.update( + skill_id="skill_123", + default_version="default_version", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.with_raw_response.update( + skill_id="", + default_version="default_version", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + skill = client.skills.list() + assert_matches_type(SyncCursorPage[Skill], skill, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + skill = client.skills.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Skill], skill, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.skills.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(SyncCursorPage[Skill], skill, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.skills.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = response.parse() + assert_matches_type(SyncCursorPage[Skill], skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + skill = client.skills.delete( + "skill_123", + ) + assert_matches_type(DeletedSkill, skill, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.skills.with_raw_response.delete( + "skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(DeletedSkill, skill, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.skills.with_streaming_response.delete( + "skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = response.parse() + assert_matches_type(DeletedSkill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + client.skills.with_raw_response.delete( + "", + ) + + +class TestAsyncSkills: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + skill = await async_client.skills.create() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + skill = await async_client.skills.create( + files=[b"raw file contents"], + ) + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = await response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + skill = await async_client.skills.retrieve( + "skill_123", + ) + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.with_raw_response.retrieve( + "skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.with_streaming_response.retrieve( + "skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = await response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + skill = await async_client.skills.update( + skill_id="skill_123", + default_version="default_version", + ) + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.with_raw_response.update( + skill_id="skill_123", + default_version="default_version", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.with_streaming_response.update( + skill_id="skill_123", + default_version="default_version", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = await response.parse() + assert_matches_type(Skill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.with_raw_response.update( + skill_id="", + default_version="default_version", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + skill = await async_client.skills.list() + assert_matches_type(AsyncCursorPage[Skill], skill, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + skill = await async_client.skills.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Skill], skill, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(AsyncCursorPage[Skill], skill, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = await response.parse() + assert_matches_type(AsyncCursorPage[Skill], skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + skill = await async_client.skills.delete( + "skill_123", + ) + assert_matches_type(DeletedSkill, skill, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.skills.with_raw_response.delete( + "skill_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + skill = response.parse() + assert_matches_type(DeletedSkill, skill, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.skills.with_streaming_response.delete( + "skill_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + skill = await response.parse() + assert_matches_type(DeletedSkill, skill, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): + await async_client.skills.with_raw_response.delete( + "", + ) From 89486d6e61b61d85e4a0a20d4f599f40ef1d4895 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 10 Feb 2026 13:12:30 -0500 Subject: [PATCH 227/408] fix merge conflict --- requirements-dev.lock | 4 ---- 1 file changed, 4 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index b4ed71101a..7e2755a021 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -170,12 +170,8 @@ requests==2.32.5 # via msal respx==0.22.0 rich==14.2.0 -<<<<<<< HEAD # via inline-snapshot ruff==0.14.7 -======= -ruff==0.14.13 ->>>>>>> origin/generated--merge-conflict six==1.17.0 # via python-dateutil sniffio==1.3.1 From f1b35ddf5ddb1ae615e34517ed01a94f54d1ec97 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 10 Feb 2026 13:16:13 -0500 Subject: [PATCH 228/408] fix one more --- requirements-dev.lock | 4 ---- 1 file changed, 4 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 7e2755a021..73312bcee1 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -215,16 +215,12 @@ typing-extensions==4.15.0 # via virtualenv typing-inspection==0.4.2 # via pydantic -<<<<<<< HEAD tzdata==2025.2 # via pandas urllib3==2.5.0 # via requests # via types-requests virtualenv==20.35.4 -======= -virtualenv==20.36.1 ->>>>>>> origin/generated--merge-conflict # via nox websockets==15.0.1 # via openai From 1c6d3d27c24a708fc5b4b2c4a743f08ff4978c35 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:17:02 +0000 Subject: [PATCH 229/408] release: 2.19.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ca3496a3e6..60b44f4d33 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.18.0" + ".": "2.19.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ad2758cf6d..be67fc97c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.19.0 (2026-02-10) + +Full Changelog: [v2.18.0...v2.19.0](https://github.com/openai/openai-python/compare/v2.18.0...v2.19.0) + +### Features + +* **api:** skills and hosted shell ([27fdf68](https://github.com/openai/openai-python/commit/27fdf6820655b5994e3c1eddb3c8d9344a8be744)) + + +### Chores + +* **internal:** bump dependencies ([fae10fd](https://github.com/openai/openai-python/commit/fae10fd6e936a044f8393a454a39906aa325a893)) + ## 2.18.0 (2026-02-09) Full Changelog: [v2.17.0...v2.18.0](https://github.com/openai/openai-python/compare/v2.17.0...v2.18.0) diff --git a/pyproject.toml b/pyproject.toml index 4dc3418464..c7821a9df4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.18.0" +version = "2.19.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index aede964362..a0c43abd1d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.18.0" # x-release-please-version +__version__ = "2.19.0" # x-release-please-version From b13d6fe944ab01b72b9a3256e48752d808c906ff Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:51:12 +0000 Subject: [PATCH 230/408] feat(api): support for images in batch api --- .stats.yml | 4 ++-- src/openai/resources/batches.py | 28 ++++++++++++++++++------- src/openai/types/batch_create_params.py | 17 +++++++++++---- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.stats.yml b/.stats.yml index e91271a6c8..6c725eaa30 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7debfce2217c66ea87bfddaa244b57de4062dd7fd766fa9a28e869614205c040.yml -openapi_spec_hash: e910fc478b8449134e2af1dc15fd33f7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-47ef7e0aaa2f2052404041650c9b4d7d8c9c51c45ef2cb081548f329c3f81a6a.yml +openapi_spec_hash: 0207b30cf74121a12c1647e25463cee9 config_hash: 8dca0f2dc2706c07cf2f8d0ed4dc062e diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 80400839e4..bc856bf5aa 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -47,7 +47,13 @@ def create( *, completion_window: Literal["24h"], endpoint: Literal[ - "/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations" + "/v1/responses", + "/v1/chat/completions", + "/v1/embeddings", + "/v1/completions", + "/v1/moderations", + "/v1/images/generations", + "/v1/images/edits", ], input_file_id: str, metadata: Optional[Metadata] | Omit = omit, @@ -68,9 +74,9 @@ def create( endpoint: The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, - and `/v1/moderations` are supported. Note that `/v1/embeddings` batches are also - restricted to a maximum of 50,000 embedding inputs across all requests in the - batch. + `/v1/moderations`, `/v1/images/generations`, and `/v1/images/edits` are + supported. Note that `/v1/embeddings` batches are also restricted to a maximum + of 50,000 embedding inputs across all requests in the batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. @@ -265,7 +271,13 @@ async def create( *, completion_window: Literal["24h"], endpoint: Literal[ - "/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations" + "/v1/responses", + "/v1/chat/completions", + "/v1/embeddings", + "/v1/completions", + "/v1/moderations", + "/v1/images/generations", + "/v1/images/edits", ], input_file_id: str, metadata: Optional[Metadata] | Omit = omit, @@ -286,9 +298,9 @@ async def create( endpoint: The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, - and `/v1/moderations` are supported. Note that `/v1/embeddings` batches are also - restricted to a maximum of 50,000 embedding inputs across all requests in the - batch. + `/v1/moderations`, `/v1/images/generations`, and `/v1/images/edits` are + supported. Note that `/v1/embeddings` batches are also restricted to a maximum + of 50,000 embedding inputs across all requests in the batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index 1088aab380..1bcd48aace 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -18,14 +18,23 @@ class BatchCreateParams(TypedDict, total=False): """ endpoint: Required[ - Literal["/v1/responses", "/v1/chat/completions", "/v1/embeddings", "/v1/completions", "/v1/moderations"] + Literal[ + "/v1/responses", + "/v1/chat/completions", + "/v1/embeddings", + "/v1/completions", + "/v1/moderations", + "/v1/images/generations", + "/v1/images/edits", + ] ] """The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, - `/v1/completions`, and `/v1/moderations` are supported. Note that - `/v1/embeddings` batches are also restricted to a maximum of 50,000 embedding - inputs across all requests in the batch. + `/v1/completions`, `/v1/moderations`, `/v1/images/generations`, and + `/v1/images/edits` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. """ input_file_id: Required[str] From f2d096fe978a1fe67f58351ddbfa0d38f3d723b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:51:50 +0000 Subject: [PATCH 231/408] release: 2.20.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 60b44f4d33..26b25a25e8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.19.0" + ".": "2.20.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index be67fc97c9..39ed325e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.20.0 (2026-02-10) + +Full Changelog: [v2.19.0...v2.20.0](https://github.com/openai/openai-python/compare/v2.19.0...v2.20.0) + +### Features + +* **api:** support for images in batch api ([28edb6e](https://github.com/openai/openai-python/commit/28edb6e1b7eb30dbb7be49979cee7882e8889264)) + ## 2.19.0 (2026-02-10) Full Changelog: [v2.18.0...v2.19.0](https://github.com/openai/openai-python/compare/v2.18.0...v2.19.0) diff --git a/pyproject.toml b/pyproject.toml index c7821a9df4..326c715f08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.19.0" +version = "2.20.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a0c43abd1d..4987d1ca18 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.19.0" # x-release-please-version +__version__ = "2.20.0" # x-release-please-version From 7b26bd3712d80e47130ee5d542895f636e48135d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 02:54:41 +0000 Subject: [PATCH 232/408] docs: update comment --- .../resources/chat/completions/completions.py | 16 ++++++---------- src/openai/types/chat/completion_list_params.py | 8 ++------ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 9c0b74b804..fb1887a7d5 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -1346,12 +1346,10 @@ def list( limit: Number of Chat Completions to retrieve. - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. + metadata: + A list of metadata keys to filter the Chat Completions by. Example: - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. + `metadata[key1]=value1&metadata[key2]=value2` model: The model used to generate the Chat Completions. @@ -2832,12 +2830,10 @@ def list( limit: Number of Chat Completions to retrieve. - metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful - for storing additional information about the object in a structured format, and - querying for objects via API or the dashboard. + metadata: + A list of metadata keys to filter the Chat Completions by. Example: - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. + `metadata[key1]=value1&metadata[key2]=value2` model: The model used to generate the Chat Completions. diff --git a/src/openai/types/chat/completion_list_params.py b/src/openai/types/chat/completion_list_params.py index 32bd3f5c0a..d93da834a3 100644 --- a/src/openai/types/chat/completion_list_params.py +++ b/src/openai/types/chat/completion_list_params.py @@ -18,13 +18,9 @@ class CompletionListParams(TypedDict, total=False): """Number of Chat Completions to retrieve.""" metadata: Optional[Metadata] - """Set of 16 key-value pairs that can be attached to an object. + """A list of metadata keys to filter the Chat Completions by. Example: - This can be useful for storing additional information about the object in a - structured format, and querying for objects via API or the dashboard. - - Keys are strings with a maximum length of 64 characters. Values are strings with - a maximum length of 512 characters. + `metadata[key1]=value1&metadata[key2]=value2` """ model: str From 957dadffc629d3980d603832864ed59df78075e8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:28:03 +0000 Subject: [PATCH 233/408] chore(internal): fix lint error on Python 3.14 --- src/openai/_utils/_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_utils/_compat.py b/src/openai/_utils/_compat.py index dd703233c5..2c70b299ce 100644 --- a/src/openai/_utils/_compat.py +++ b/src/openai/_utils/_compat.py @@ -26,7 +26,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool: else: import types - return tp is Union or tp is types.UnionType + return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap] def is_typeddict(tp: Type[Any]) -> bool: From 01c9eee2cb3fb0ec14d9026f93fb6a71d432e3a3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 20:45:28 +0000 Subject: [PATCH 234/408] codegen metadata --- .stats.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6c725eaa30..9a081e2ac0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-47ef7e0aaa2f2052404041650c9b4d7d8c9c51c45ef2cb081548f329c3f81a6a.yml -openapi_spec_hash: 0207b30cf74121a12c1647e25463cee9 -config_hash: 8dca0f2dc2706c07cf2f8d0ed4dc062e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-1595c7e4e1d7bf7eae2e49ad800316e13b69ffb18bff2a2f55236fce792deb53.yml +openapi_spec_hash: c3a3a0d8d4bbf11179149244f017a6dc +config_hash: f6cdeee28c96db61b0a5d92e414bc0ae From d81ee8f85207bdc88c2749ae9d0644d9a997e397 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 22:05:45 +0000 Subject: [PATCH 235/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 9a081e2ac0..dcc9c27df3 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-1595c7e4e1d7bf7eae2e49ad800316e13b69ffb18bff2a2f55236fce792deb53.yml openapi_spec_hash: c3a3a0d8d4bbf11179149244f017a6dc -config_hash: f6cdeee28c96db61b0a5d92e414bc0ae +config_hash: 948733484caf41e71093c6582dbc319c From 72e1e15abfc283fa2714bc4d61e4a31740833084 Mon Sep 17 00:00:00 2001 From: Kar Petrosyan <92274156+karpetrosyan@users.noreply.github.com> Date: Fri, 13 Feb 2026 14:47:25 +0400 Subject: [PATCH 236/408] fix(structured outputs): resolve memory leak in parse methods (#2860) --- src/openai/lib/_parsing/__init__.py | 1 - src/openai/lib/_parsing/_completions.py | 31 ++------ src/openai/lib/_parsing/_responses.py | 24 +++---- src/openai/lib/streaming/chat/_completions.py | 3 +- tests/lib/chat/test_completions.py | 72 +++++++++---------- tests/lib/chat/test_completions_streaming.py | 66 ++++++++--------- tests/lib/utils.py | 30 ++------ 7 files changed, 92 insertions(+), 135 deletions(-) diff --git a/src/openai/lib/_parsing/__init__.py b/src/openai/lib/_parsing/__init__.py index 4d454c3a20..08591f43f4 100644 --- a/src/openai/lib/_parsing/__init__.py +++ b/src/openai/lib/_parsing/__init__.py @@ -6,7 +6,6 @@ validate_input_tools as validate_input_tools, parse_chat_completion as parse_chat_completion, get_input_tool_by_name as get_input_tool_by_name, - solve_response_format_t as solve_response_format_t, parse_function_tool_arguments as parse_function_tool_arguments, type_to_response_format_param as type_to_response_format_param, ) diff --git a/src/openai/lib/_parsing/_completions.py b/src/openai/lib/_parsing/_completions.py index 7903732a4a..7a1bded1de 100644 --- a/src/openai/lib/_parsing/_completions.py +++ b/src/openai/lib/_parsing/_completions.py @@ -138,7 +138,7 @@ def parse_chat_completion( choices.append( construct_type_unchecked( - type_=cast(Any, ParsedChoice)[solve_response_format_t(response_format)], + type_=ParsedChoice[ResponseFormatT], value={ **choice.to_dict(), "message": { @@ -153,15 +153,12 @@ def parse_chat_completion( ) ) - return cast( - ParsedChatCompletion[ResponseFormatT], - construct_type_unchecked( - type_=cast(Any, ParsedChatCompletion)[solve_response_format_t(response_format)], - value={ - **chat_completion.to_dict(), - "choices": choices, - }, - ), + return construct_type_unchecked( + type_=ParsedChatCompletion[ResponseFormatT], + value={ + **chat_completion.to_dict(), + "choices": choices, + }, ) @@ -201,20 +198,6 @@ def maybe_parse_content( return None -def solve_response_format_t( - response_format: type[ResponseFormatT] | ResponseFormatParam | Omit, -) -> type[ResponseFormatT]: - """Return the runtime type for the given response format. - - If no response format is given, or if we won't auto-parse the response format - then we default to `None`. - """ - if has_rich_response_format(response_format): - return response_format - - return cast("type[ResponseFormatT]", _default_response_format) - - def has_parseable_input( *, response_format: type | ResponseFormatParam | Omit, diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 4bed171df7..5676eb0b63 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import TYPE_CHECKING, Any, List, Iterable, cast +from typing import TYPE_CHECKING, List, Iterable, cast from typing_extensions import TypeVar, assert_never import pydantic @@ -12,7 +12,7 @@ from ..._compat import PYDANTIC_V1, model_parse_json from ..._models import construct_type_unchecked from .._pydantic import is_basemodel_type, is_dataclass_like_type -from ._completions import solve_response_format_t, type_to_response_format_param +from ._completions import type_to_response_format_param from ...types.responses import ( Response, ToolParam, @@ -56,7 +56,6 @@ def parse_response( input_tools: Iterable[ToolParam] | Omit | None, response: Response | ParsedResponse[object], ) -> ParsedResponse[TextFormatT]: - solved_t = solve_response_format_t(text_format) output_list: List[ParsedResponseOutputItem[TextFormatT]] = [] for output in response.output: @@ -69,7 +68,7 @@ def parse_response( content_list.append( construct_type_unchecked( - type_=cast(Any, ParsedResponseOutputText)[solved_t], + type_=ParsedResponseOutputText[TextFormatT], value={ **item.to_dict(), "parsed": parse_text(item.text, text_format=text_format), @@ -79,7 +78,7 @@ def parse_response( output_list.append( construct_type_unchecked( - type_=cast(Any, ParsedResponseOutputMessage)[solved_t], + type_=ParsedResponseOutputMessage[TextFormatT], value={ **output.to_dict(), "content": content_list, @@ -123,15 +122,12 @@ def parse_response( else: output_list.append(output) - return cast( - ParsedResponse[TextFormatT], - construct_type_unchecked( - type_=cast(Any, ParsedResponse)[solved_t], - value={ - **response.to_dict(), - "output": output_list, - }, - ), + return construct_type_unchecked( + type_=ParsedResponse[TextFormatT], + value={ + **response.to_dict(), + "output": output_list, + }, ) diff --git a/src/openai/lib/streaming/chat/_completions.py b/src/openai/lib/streaming/chat/_completions.py index c4610e2120..5f072cafbd 100644 --- a/src/openai/lib/streaming/chat/_completions.py +++ b/src/openai/lib/streaming/chat/_completions.py @@ -33,7 +33,6 @@ maybe_parse_content, parse_chat_completion, get_input_tool_by_name, - solve_response_format_t, parse_function_tool_arguments, ) from ...._streaming import Stream, AsyncStream @@ -663,7 +662,7 @@ def _content_done_events( # type variable, e.g. `ContentDoneEvent[MyModelType]` cast( # pyright: ignore[reportUnnecessaryCast] "type[ContentDoneEvent[ResponseFormatT]]", - cast(Any, ContentDoneEvent)[solve_response_format_t(response_format)], + cast(Any, ContentDoneEvent), ), type="content.done", content=choice_snapshot.message.content, diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index afad5a1391..85bab4f095 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -50,13 +50,13 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte assert print_obj(completion, monkeypatch) == snapshot( """\ -ParsedChatCompletion[NoneType]( +ParsedChatCompletion( choices=[ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I @@ -120,13 +120,13 @@ class Location(BaseModel): assert print_obj(completion, monkeypatch) == snapshot( """\ -ParsedChatCompletion[Location]( +ParsedChatCompletion( choices=[ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', @@ -191,13 +191,13 @@ class Location(BaseModel): assert print_obj(completion, monkeypatch) == snapshot( """\ -ParsedChatCompletion[Location]( +ParsedChatCompletion( choices=[ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', @@ -266,11 +266,11 @@ class ColorDetection(BaseModel): assert print_obj(completion.choices[0], monkeypatch) == snapshot( """\ -ParsedChoice[ColorDetection]( +ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[ColorDetection]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"color":"red","hex_color_code":"#FF0000"}', @@ -317,11 +317,11 @@ class Location(BaseModel): assert print_obj(completion.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":64,"units":"f"}', @@ -332,11 +332,11 @@ class Location(BaseModel): tool_calls=None ) ), - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=1, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', @@ -347,11 +347,11 @@ class Location(BaseModel): tool_calls=None ) ), - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=2, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":63.0,"units":"f"}', @@ -397,13 +397,13 @@ class CalendarEvent: assert print_obj(completion, monkeypatch) == snapshot( """\ -ParsedChatCompletion[CalendarEvent]( +ParsedChatCompletion( choices=[ - ParsedChoice[CalendarEvent]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[CalendarEvent]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"name":"Science Fair","date":"Friday","participants":["Alice","Bob"]}', @@ -462,11 +462,11 @@ def test_pydantic_tool_model_all_types(client: OpenAI, respx_mock: MockRouter, m assert print_obj(completion.choices[0], monkeypatch) == snapshot( """\ -ParsedChoice[Query]( +ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Query]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -576,11 +576,11 @@ class Location(BaseModel): assert print_obj(completion.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -627,11 +627,11 @@ class GetWeatherArgs(BaseModel): assert print_obj(completion.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -701,11 +701,11 @@ class GetStockPrice(BaseModel): assert print_obj(completion.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -784,11 +784,11 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: assert print_obj(completion.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -866,13 +866,13 @@ class Location(BaseModel): assert isinstance(message.parsed.city, str) assert print_obj(completion, monkeypatch) == snapshot( """\ -ParsedChatCompletion[Location]( +ParsedChatCompletion( choices=[ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":58,"units":"f"}', @@ -943,13 +943,13 @@ class Location(BaseModel): assert isinstance(message.parsed.city, str) assert print_obj(completion, monkeypatch) == snapshot( """\ -ParsedChatCompletion[Location]( +ParsedChatCompletion( choices=[ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index 548416dfe2..eb3a0973ac 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -63,11 +63,11 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I @@ -84,7 +84,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte ) assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( """\ -ContentDoneEvent[NoneType]( +ContentDoneEvent( content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I recommend checking a reliable weather website or a weather app.", parsed=None, @@ -140,13 +140,13 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream assert print_obj(listener.stream.get_final_completion(), monkeypatch) == snapshot( """\ -ParsedChatCompletion[Location]( +ParsedChatCompletion( choices=[ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":61,"units":"f"}', @@ -181,7 +181,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream ) assert print_obj(listener.get_event_by_type("content.done"), monkeypatch) == snapshot( """\ -ContentDoneEvent[Location]( +ContentDoneEvent( content='{"city":"San Francisco","temperature":61,"units":"f"}', parsed=Location(city='San Francisco', temperature=61.0, units='f'), type='content.done' @@ -320,11 +320,11 @@ class Location(BaseModel): assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":65,"units":"f"}', @@ -335,11 +335,11 @@ class Location(BaseModel): tool_calls=None ) ), - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=1, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":61,"units":"f"}', @@ -350,11 +350,11 @@ class Location(BaseModel): tool_calls=None ) ), - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=2, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='{"city":"San Francisco","temperature":59,"units":"f"}', @@ -426,11 +426,11 @@ class Location(BaseModel): assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -495,7 +495,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot("""\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='stop', index=0, logprobs=ChoiceLogprobs( @@ -505,7 +505,7 @@ def test_content_logprobs_events(client: OpenAI, respx_mock: MockRouter, monkeyp ], refusal=None ), - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='Foo!', @@ -563,7 +563,7 @@ class Location(BaseModel): assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot("""\ [ - ParsedChoice[Location]( + ParsedChoice( finish_reason='stop', index=0, logprobs=ChoiceLogprobs( @@ -617,7 +617,7 @@ class Location(BaseModel): ChatCompletionTokenLogprob(bytes=[46], logprob=-0.57687104, token='.', top_logprobs=[]) ] ), - message=ParsedChatCompletionMessage[Location]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -660,11 +660,11 @@ class GetWeatherArgs(BaseModel): assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[object]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[object]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -693,11 +693,11 @@ class GetWeatherArgs(BaseModel): assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -765,11 +765,11 @@ class GetStockPrice(BaseModel): assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[object]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[object]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -874,11 +874,11 @@ def test_parse_strict_tools(client: OpenAI, respx_mock: MockRouter, monkeypatch: assert print_obj(listener.stream.current_completion_snapshot.choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[object]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[object]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -926,11 +926,11 @@ def test_non_pydantic_response_format(client: OpenAI, respx_mock: MockRouter, mo assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content='\\n {\\n "location": "San Francisco, CA",\\n "weather": {\\n "temperature": "18°C",\\n @@ -987,11 +987,11 @@ def test_allows_non_strict_tools_but_no_parsing( assert print_obj(listener.stream.get_final_completion().choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='tool_calls', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content=None, @@ -1047,11 +1047,11 @@ def streamer(client: OpenAI) -> Iterator[ChatCompletionChunk]: assert print_obj(state.get_final_completion().choices, monkeypatch) == snapshot( """\ [ - ParsedChoice[NoneType]( + ParsedChoice( finish_reason='stop', index=0, logprobs=None, - message=ParsedChatCompletionMessage[NoneType]( + message=ParsedChatCompletionMessage( annotations=None, audio=None, content="I'm unable to provide real-time weather updates. To get the current weather in San Francisco, I diff --git a/tests/lib/utils.py b/tests/lib/utils.py index e6b6a29434..0efdfca968 100644 --- a/tests/lib/utils.py +++ b/tests/lib/utils.py @@ -1,6 +1,6 @@ from __future__ import annotations -import inspect +import re from typing import Any, Iterable from typing_extensions import TypeAlias @@ -28,27 +28,7 @@ def __repr_args__(self: pydantic.BaseModel) -> ReprArgs: string = rich_print_str(obj) - # we remove all `fn_name..` occurrences - # so that we can share the same snapshots between - # pydantic v1 and pydantic v2 as their output for - # generic models differs, e.g. - # - # v2: `ParsedChatCompletion[test_parse_pydantic_model..Location]` - # v1: `ParsedChatCompletion[Location]` - return clear_locals(string, stacklevel=2) - - -def get_caller_name(*, stacklevel: int = 1) -> str: - frame = inspect.currentframe() - assert frame is not None - - for i in range(stacklevel): - frame = frame.f_back - assert frame is not None, f"no {i}th frame" - - return frame.f_code.co_name - - -def clear_locals(string: str, *, stacklevel: int) -> str: - caller = get_caller_name(stacklevel=stacklevel + 1) - return string.replace(f"{caller}..", "") + # Pydantic v1 and v2 have different implementations of __repr__ and print out + # generics differently, so we strip out generic type parameters to ensure + # consistent snapshot tests across both versions + return re.sub(r"([A-Za-z_]\w*)\[[^\[\]]+\](?=\()", r"\1", string) From 156d51801b051277708763124a1b7afe61ffa171 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 23:25:40 +0000 Subject: [PATCH 237/408] docs: split `api.md` by standalone resources --- api.md | 382 +----------------- pyproject.toml | 2 +- src/openai/_client.py | 2 +- src/openai/_module_client.py | 2 +- src/openai/resources/conversations/api.md | 42 ++ src/openai/resources/realtime/api.md | 137 +++++++ src/openai/resources/responses/api.md | 175 ++++++++ src/openai/resources/webhooks/__init__.py | 5 + src/openai/resources/webhooks/api.md | 24 ++ .../resources/{ => webhooks}/webhooks.py | 12 +- tests/api_resources/webhooks/__init__.py | 1 + 11 files changed, 397 insertions(+), 387 deletions(-) create mode 100644 src/openai/resources/conversations/api.md create mode 100644 src/openai/resources/realtime/api.md create mode 100644 src/openai/resources/responses/api.md create mode 100644 src/openai/resources/webhooks/__init__.py create mode 100644 src/openai/resources/webhooks/api.md rename src/openai/resources/{ => webhooks}/webhooks.py (96%) create mode 100644 tests/api_resources/webhooks/__init__.py diff --git a/api.md b/api.md index 54fa94da5f..da521d3418 100644 --- a/api.md +++ b/api.md @@ -419,30 +419,7 @@ Methods: - client.vector_stores.file_batches.poll(\*args) -> VectorStoreFileBatch - client.vector_stores.file_batches.upload_and_poll(\*args) -> VectorStoreFileBatch -# Webhooks - -Types: - -```python -from openai.types.webhooks import ( - BatchCancelledWebhookEvent, - BatchCompletedWebhookEvent, - BatchExpiredWebhookEvent, - BatchFailedWebhookEvent, - EvalRunCanceledWebhookEvent, - EvalRunFailedWebhookEvent, - EvalRunSucceededWebhookEvent, - FineTuningJobCancelledWebhookEvent, - FineTuningJobFailedWebhookEvent, - FineTuningJobSucceededWebhookEvent, - RealtimeCallIncomingWebhookEvent, - ResponseCancelledWebhookEvent, - ResponseCompletedWebhookEvent, - ResponseFailedWebhookEvent, - ResponseIncompleteWebhookEvent, - UnwrapWebhookEvent, -) -``` +# [Webhooks](src/openai/resources/webhooks/api.md) Methods: @@ -727,362 +704,11 @@ Methods: - client.uploads.parts.create(upload_id, \*\*params) -> UploadPart -# Responses - -Types: - -```python -from openai.types.responses import ( - ApplyPatchTool, - CompactedResponse, - ComputerTool, - ContainerAuto, - ContainerNetworkPolicyAllowlist, - ContainerNetworkPolicyDisabled, - ContainerNetworkPolicyDomainSecret, - ContainerReference, - CustomTool, - EasyInputMessage, - FileSearchTool, - FunctionShellTool, - FunctionTool, - InlineSkill, - InlineSkillSource, - LocalEnvironment, - LocalSkill, - Response, - ResponseApplyPatchToolCall, - ResponseApplyPatchToolCallOutput, - ResponseAudioDeltaEvent, - ResponseAudioDoneEvent, - ResponseAudioTranscriptDeltaEvent, - ResponseAudioTranscriptDoneEvent, - ResponseCodeInterpreterCallCodeDeltaEvent, - ResponseCodeInterpreterCallCodeDoneEvent, - ResponseCodeInterpreterCallCompletedEvent, - ResponseCodeInterpreterCallInProgressEvent, - ResponseCodeInterpreterCallInterpretingEvent, - ResponseCodeInterpreterToolCall, - ResponseCompactionItem, - ResponseCompactionItemParam, - ResponseCompletedEvent, - ResponseComputerToolCall, - ResponseComputerToolCallOutputItem, - ResponseComputerToolCallOutputScreenshot, - ResponseContainerReference, - ResponseContent, - ResponseContentPartAddedEvent, - ResponseContentPartDoneEvent, - ResponseConversationParam, - ResponseCreatedEvent, - ResponseCustomToolCall, - ResponseCustomToolCallInputDeltaEvent, - ResponseCustomToolCallInputDoneEvent, - ResponseCustomToolCallOutput, - ResponseError, - ResponseErrorEvent, - ResponseFailedEvent, - ResponseFileSearchCallCompletedEvent, - ResponseFileSearchCallInProgressEvent, - ResponseFileSearchCallSearchingEvent, - ResponseFileSearchToolCall, - ResponseFormatTextConfig, - ResponseFormatTextJSONSchemaConfig, - ResponseFunctionCallArgumentsDeltaEvent, - ResponseFunctionCallArgumentsDoneEvent, - ResponseFunctionCallOutputItem, - ResponseFunctionCallOutputItemList, - ResponseFunctionShellCallOutputContent, - ResponseFunctionShellToolCall, - ResponseFunctionShellToolCallOutput, - ResponseFunctionToolCall, - ResponseFunctionToolCallItem, - ResponseFunctionToolCallOutputItem, - ResponseFunctionWebSearch, - ResponseImageGenCallCompletedEvent, - ResponseImageGenCallGeneratingEvent, - ResponseImageGenCallInProgressEvent, - ResponseImageGenCallPartialImageEvent, - ResponseInProgressEvent, - ResponseIncludable, - ResponseIncompleteEvent, - ResponseInput, - ResponseInputAudio, - ResponseInputContent, - ResponseInputFile, - ResponseInputFileContent, - ResponseInputImage, - ResponseInputImageContent, - ResponseInputItem, - ResponseInputMessageContentList, - ResponseInputMessageItem, - ResponseInputText, - ResponseInputTextContent, - ResponseItem, - ResponseLocalEnvironment, - ResponseMcpCallArgumentsDeltaEvent, - ResponseMcpCallArgumentsDoneEvent, - ResponseMcpCallCompletedEvent, - ResponseMcpCallFailedEvent, - ResponseMcpCallInProgressEvent, - ResponseMcpListToolsCompletedEvent, - ResponseMcpListToolsFailedEvent, - ResponseMcpListToolsInProgressEvent, - ResponseOutputAudio, - ResponseOutputItem, - ResponseOutputItemAddedEvent, - ResponseOutputItemDoneEvent, - ResponseOutputMessage, - ResponseOutputRefusal, - ResponseOutputText, - ResponseOutputTextAnnotationAddedEvent, - ResponsePrompt, - ResponseQueuedEvent, - ResponseReasoningItem, - ResponseReasoningSummaryPartAddedEvent, - ResponseReasoningSummaryPartDoneEvent, - ResponseReasoningSummaryTextDeltaEvent, - ResponseReasoningSummaryTextDoneEvent, - ResponseReasoningTextDeltaEvent, - ResponseReasoningTextDoneEvent, - ResponseRefusalDeltaEvent, - ResponseRefusalDoneEvent, - ResponseStatus, - ResponseStreamEvent, - ResponseTextConfig, - ResponseTextDeltaEvent, - ResponseTextDoneEvent, - ResponseUsage, - ResponseWebSearchCallCompletedEvent, - ResponseWebSearchCallInProgressEvent, - ResponseWebSearchCallSearchingEvent, - SkillReference, - Tool, - ToolChoiceAllowed, - ToolChoiceApplyPatch, - ToolChoiceCustom, - ToolChoiceFunction, - ToolChoiceMcp, - ToolChoiceOptions, - ToolChoiceShell, - ToolChoiceTypes, - WebSearchPreviewTool, - WebSearchTool, -) -``` - -Methods: - -- client.responses.create(\*\*params) -> Response -- client.responses.retrieve(response_id, \*\*params) -> Response -- client.responses.delete(response_id) -> None -- client.responses.cancel(response_id) -> Response -- client.responses.compact(\*\*params) -> CompactedResponse - -## InputItems - -Types: - -```python -from openai.types.responses import ResponseItemList -``` - -Methods: - -- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] - -## InputTokens - -Types: - -```python -from openai.types.responses import InputTokenCountResponse -``` - -Methods: - -- client.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse - -# Realtime - -Types: - -```python -from openai.types.realtime import ( - AudioTranscription, - ConversationCreatedEvent, - ConversationItem, - ConversationItemAdded, - ConversationItemCreateEvent, - ConversationItemCreatedEvent, - ConversationItemDeleteEvent, - ConversationItemDeletedEvent, - ConversationItemDone, - ConversationItemInputAudioTranscriptionCompletedEvent, - ConversationItemInputAudioTranscriptionDeltaEvent, - ConversationItemInputAudioTranscriptionFailedEvent, - ConversationItemInputAudioTranscriptionSegment, - ConversationItemRetrieveEvent, - ConversationItemTruncateEvent, - ConversationItemTruncatedEvent, - ConversationItemWithReference, - InputAudioBufferAppendEvent, - InputAudioBufferClearEvent, - InputAudioBufferClearedEvent, - InputAudioBufferCommitEvent, - InputAudioBufferCommittedEvent, - InputAudioBufferDtmfEventReceivedEvent, - InputAudioBufferSpeechStartedEvent, - InputAudioBufferSpeechStoppedEvent, - InputAudioBufferTimeoutTriggered, - LogProbProperties, - McpListToolsCompleted, - McpListToolsFailed, - McpListToolsInProgress, - NoiseReductionType, - OutputAudioBufferClearEvent, - RateLimitsUpdatedEvent, - RealtimeAudioConfig, - RealtimeAudioConfigInput, - RealtimeAudioConfigOutput, - RealtimeAudioFormats, - RealtimeAudioInputTurnDetection, - RealtimeClientEvent, - RealtimeConversationItemAssistantMessage, - RealtimeConversationItemFunctionCall, - RealtimeConversationItemFunctionCallOutput, - RealtimeConversationItemSystemMessage, - RealtimeConversationItemUserMessage, - RealtimeError, - RealtimeErrorEvent, - RealtimeFunctionTool, - RealtimeMcpApprovalRequest, - RealtimeMcpApprovalResponse, - RealtimeMcpListTools, - RealtimeMcpProtocolError, - RealtimeMcpToolCall, - RealtimeMcpToolExecutionError, - RealtimeMcphttpError, - RealtimeResponse, - RealtimeResponseCreateAudioOutput, - RealtimeResponseCreateMcpTool, - RealtimeResponseCreateParams, - RealtimeResponseStatus, - RealtimeResponseUsage, - RealtimeResponseUsageInputTokenDetails, - RealtimeResponseUsageOutputTokenDetails, - RealtimeServerEvent, - RealtimeSession, - RealtimeSessionCreateRequest, - RealtimeToolChoiceConfig, - RealtimeToolsConfig, - RealtimeToolsConfigUnion, - RealtimeTracingConfig, - RealtimeTranscriptionSessionAudio, - RealtimeTranscriptionSessionAudioInput, - RealtimeTranscriptionSessionAudioInputTurnDetection, - RealtimeTranscriptionSessionCreateRequest, - RealtimeTruncation, - RealtimeTruncationRetentionRatio, - ResponseAudioDeltaEvent, - ResponseAudioDoneEvent, - ResponseAudioTranscriptDeltaEvent, - ResponseAudioTranscriptDoneEvent, - ResponseCancelEvent, - ResponseContentPartAddedEvent, - ResponseContentPartDoneEvent, - ResponseCreateEvent, - ResponseCreatedEvent, - ResponseDoneEvent, - ResponseFunctionCallArgumentsDeltaEvent, - ResponseFunctionCallArgumentsDoneEvent, - ResponseMcpCallArgumentsDelta, - ResponseMcpCallArgumentsDone, - ResponseMcpCallCompleted, - ResponseMcpCallFailed, - ResponseMcpCallInProgress, - ResponseOutputItemAddedEvent, - ResponseOutputItemDoneEvent, - ResponseTextDeltaEvent, - ResponseTextDoneEvent, - SessionCreatedEvent, - SessionUpdateEvent, - SessionUpdatedEvent, - TranscriptionSessionUpdate, - TranscriptionSessionUpdatedEvent, -) -``` - -## ClientSecrets +# [Responses](src/openai/resources/responses/api.md) -Types: - -```python -from openai.types.realtime import ( - RealtimeSessionClientSecret, - RealtimeSessionCreateResponse, - RealtimeTranscriptionSessionCreateResponse, - RealtimeTranscriptionSessionTurnDetection, - ClientSecretCreateResponse, -) -``` - -Methods: - -- client.realtime.client_secrets.create(\*\*params) -> ClientSecretCreateResponse - -## Calls - -Methods: - -- client.realtime.calls.create(\*\*params) -> HttpxBinaryResponseContent -- client.realtime.calls.accept(call_id, \*\*params) -> None -- client.realtime.calls.hangup(call_id) -> None -- client.realtime.calls.refer(call_id, \*\*params) -> None -- client.realtime.calls.reject(call_id, \*\*params) -> None - -# Conversations - -Types: - -```python -from openai.types.conversations import ( - ComputerScreenshotContent, - Conversation, - ConversationDeleted, - ConversationDeletedResource, - Message, - SummaryTextContent, - TextContent, - InputTextContent, - OutputTextContent, - RefusalContent, - InputImageContent, - InputFileContent, -) -``` - -Methods: - -- client.conversations.create(\*\*params) -> Conversation -- client.conversations.retrieve(conversation_id) -> Conversation -- client.conversations.update(conversation_id, \*\*params) -> Conversation -- client.conversations.delete(conversation_id) -> ConversationDeletedResource - -## Items - -Types: - -```python -from openai.types.conversations import ConversationItem, ConversationItemList -``` - -Methods: +# [Realtime](src/openai/resources/realtime/api.md) -- client.conversations.items.create(conversation_id, \*\*params) -> ConversationItemList -- client.conversations.items.retrieve(item_id, \*, conversation_id, \*\*params) -> ConversationItem -- client.conversations.items.list(conversation_id, \*\*params) -> SyncConversationCursorPage[ConversationItem] -- client.conversations.items.delete(item_id, \*, conversation_id) -> Conversation +# [Conversations](src/openai/resources/conversations/api.md) # Evals diff --git a/pyproject.toml b/pyproject.toml index 326c715f08..ed8ee459cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ format = { chain = [ # run formatting again to fix any inconsistencies when imports are stripped "format:ruff", ]} -"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" +"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'" "format:ruff" = "ruff format" "lint" = { chain = [ diff --git a/src/openai/_client.py b/src/openai/_client.py index 440a8a45c8..0399bbf742 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -63,7 +63,6 @@ from .resources.models import Models, AsyncModels from .resources.videos import Videos, AsyncVideos from .resources.batches import Batches, AsyncBatches - from .resources.webhooks import Webhooks, AsyncWebhooks from .resources.beta.beta import Beta, AsyncBeta from .resources.chat.chat import Chat, AsyncChat from .resources.embeddings import Embeddings, AsyncEmbeddings @@ -74,6 +73,7 @@ from .resources.skills.skills import Skills, AsyncSkills from .resources.uploads.uploads import Uploads, AsyncUploads from .resources.realtime.realtime import Realtime, AsyncRealtime + from .resources.webhooks.webhooks import Webhooks, AsyncWebhooks from .resources.responses.responses import Responses, AsyncResponses from .resources.containers.containers import Containers, AsyncContainers from .resources.fine_tuning.fine_tuning import FineTuning, AsyncFineTuning diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index 17bb9306aa..98901c0446 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -11,7 +11,6 @@ from .resources.models import Models from .resources.videos import Videos from .resources.batches import Batches - from .resources.webhooks import Webhooks from .resources.beta.beta import Beta from .resources.chat.chat import Chat from .resources.embeddings import Embeddings @@ -22,6 +21,7 @@ from .resources.skills.skills import Skills from .resources.uploads.uploads import Uploads from .resources.realtime.realtime import Realtime + from .resources.webhooks.webhooks import Webhooks from .resources.responses.responses import Responses from .resources.containers.containers import Containers from .resources.fine_tuning.fine_tuning import FineTuning diff --git a/src/openai/resources/conversations/api.md b/src/openai/resources/conversations/api.md new file mode 100644 index 0000000000..9e9181a367 --- /dev/null +++ b/src/openai/resources/conversations/api.md @@ -0,0 +1,42 @@ +# Conversations + +Types: + +```python +from openai.types.conversations import ( + ComputerScreenshotContent, + Conversation, + ConversationDeleted, + ConversationDeletedResource, + Message, + SummaryTextContent, + TextContent, + InputTextContent, + OutputTextContent, + RefusalContent, + InputImageContent, + InputFileContent, +) +``` + +Methods: + +- client.conversations.create(\*\*params) -> Conversation +- client.conversations.retrieve(conversation_id) -> Conversation +- client.conversations.update(conversation_id, \*\*params) -> Conversation +- client.conversations.delete(conversation_id) -> ConversationDeletedResource + +## Items + +Types: + +```python +from openai.types.conversations import ConversationItem, ConversationItemList +``` + +Methods: + +- client.conversations.items.create(conversation_id, \*\*params) -> ConversationItemList +- client.conversations.items.retrieve(item_id, \*, conversation_id, \*\*params) -> ConversationItem +- client.conversations.items.list(conversation_id, \*\*params) -> SyncConversationCursorPage[ConversationItem] +- client.conversations.items.delete(item_id, \*, conversation_id) -> Conversation diff --git a/src/openai/resources/realtime/api.md b/src/openai/resources/realtime/api.md new file mode 100644 index 0000000000..1a178384db --- /dev/null +++ b/src/openai/resources/realtime/api.md @@ -0,0 +1,137 @@ +# Realtime + +Types: + +```python +from openai.types.realtime import ( + AudioTranscription, + ConversationCreatedEvent, + ConversationItem, + ConversationItemAdded, + ConversationItemCreateEvent, + ConversationItemCreatedEvent, + ConversationItemDeleteEvent, + ConversationItemDeletedEvent, + ConversationItemDone, + ConversationItemInputAudioTranscriptionCompletedEvent, + ConversationItemInputAudioTranscriptionDeltaEvent, + ConversationItemInputAudioTranscriptionFailedEvent, + ConversationItemInputAudioTranscriptionSegment, + ConversationItemRetrieveEvent, + ConversationItemTruncateEvent, + ConversationItemTruncatedEvent, + ConversationItemWithReference, + InputAudioBufferAppendEvent, + InputAudioBufferClearEvent, + InputAudioBufferClearedEvent, + InputAudioBufferCommitEvent, + InputAudioBufferCommittedEvent, + InputAudioBufferDtmfEventReceivedEvent, + InputAudioBufferSpeechStartedEvent, + InputAudioBufferSpeechStoppedEvent, + InputAudioBufferTimeoutTriggered, + LogProbProperties, + McpListToolsCompleted, + McpListToolsFailed, + McpListToolsInProgress, + NoiseReductionType, + OutputAudioBufferClearEvent, + RateLimitsUpdatedEvent, + RealtimeAudioConfig, + RealtimeAudioConfigInput, + RealtimeAudioConfigOutput, + RealtimeAudioFormats, + RealtimeAudioInputTurnDetection, + RealtimeClientEvent, + RealtimeConversationItemAssistantMessage, + RealtimeConversationItemFunctionCall, + RealtimeConversationItemFunctionCallOutput, + RealtimeConversationItemSystemMessage, + RealtimeConversationItemUserMessage, + RealtimeError, + RealtimeErrorEvent, + RealtimeFunctionTool, + RealtimeMcpApprovalRequest, + RealtimeMcpApprovalResponse, + RealtimeMcpListTools, + RealtimeMcpProtocolError, + RealtimeMcpToolCall, + RealtimeMcpToolExecutionError, + RealtimeMcphttpError, + RealtimeResponse, + RealtimeResponseCreateAudioOutput, + RealtimeResponseCreateMcpTool, + RealtimeResponseCreateParams, + RealtimeResponseStatus, + RealtimeResponseUsage, + RealtimeResponseUsageInputTokenDetails, + RealtimeResponseUsageOutputTokenDetails, + RealtimeServerEvent, + RealtimeSession, + RealtimeSessionCreateRequest, + RealtimeToolChoiceConfig, + RealtimeToolsConfig, + RealtimeToolsConfigUnion, + RealtimeTracingConfig, + RealtimeTranscriptionSessionAudio, + RealtimeTranscriptionSessionAudioInput, + RealtimeTranscriptionSessionAudioInputTurnDetection, + RealtimeTranscriptionSessionCreateRequest, + RealtimeTruncation, + RealtimeTruncationRetentionRatio, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCancelEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreateEvent, + ResponseCreatedEvent, + ResponseDoneEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseMcpCallArgumentsDelta, + ResponseMcpCallArgumentsDone, + ResponseMcpCallCompleted, + ResponseMcpCallFailed, + ResponseMcpCallInProgress, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + SessionCreatedEvent, + SessionUpdateEvent, + SessionUpdatedEvent, + TranscriptionSessionUpdate, + TranscriptionSessionUpdatedEvent, +) +``` + +## ClientSecrets + +Types: + +```python +from openai.types.realtime import ( + RealtimeSessionClientSecret, + RealtimeSessionCreateResponse, + RealtimeTranscriptionSessionCreateResponse, + RealtimeTranscriptionSessionTurnDetection, + ClientSecretCreateResponse, +) +``` + +Methods: + +- client.realtime.client_secrets.create(\*\*params) -> ClientSecretCreateResponse + +## Calls + +Methods: + +- client.realtime.calls.create(\*\*params) -> HttpxBinaryResponseContent +- client.realtime.calls.accept(call_id, \*\*params) -> None +- client.realtime.calls.hangup(call_id) -> None +- client.realtime.calls.refer(call_id, \*\*params) -> None +- client.realtime.calls.reject(call_id, \*\*params) -> None diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md new file mode 100644 index 0000000000..6eb1b999fa --- /dev/null +++ b/src/openai/resources/responses/api.md @@ -0,0 +1,175 @@ +# Responses + +Types: + +```python +from openai.types.responses import ( + ApplyPatchTool, + CompactedResponse, + ComputerTool, + ContainerAuto, + ContainerNetworkPolicyAllowlist, + ContainerNetworkPolicyDisabled, + ContainerNetworkPolicyDomainSecret, + ContainerReference, + CustomTool, + EasyInputMessage, + FileSearchTool, + FunctionShellTool, + FunctionTool, + InlineSkill, + InlineSkillSource, + LocalEnvironment, + LocalSkill, + Response, + ResponseApplyPatchToolCall, + ResponseApplyPatchToolCallOutput, + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCodeInterpreterToolCall, + ResponseCompactionItem, + ResponseCompactionItemParam, + ResponseCompletedEvent, + ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, + ResponseComputerToolCallOutputScreenshot, + ResponseContainerReference, + ResponseContent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseConversationParam, + ResponseCreatedEvent, + ResponseCustomToolCall, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, + ResponseCustomToolCallOutput, + ResponseError, + ResponseErrorEvent, + ResponseFailedEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFileSearchToolCall, + ResponseFormatTextConfig, + ResponseFormatTextJSONSchemaConfig, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseFunctionCallOutputItem, + ResponseFunctionCallOutputItemList, + ResponseFunctionShellCallOutputContent, + ResponseFunctionShellToolCall, + ResponseFunctionShellToolCallOutput, + ResponseFunctionToolCall, + ResponseFunctionToolCallItem, + ResponseFunctionToolCallOutputItem, + ResponseFunctionWebSearch, + ResponseImageGenCallCompletedEvent, + ResponseImageGenCallGeneratingEvent, + ResponseImageGenCallInProgressEvent, + ResponseImageGenCallPartialImageEvent, + ResponseInProgressEvent, + ResponseIncludable, + ResponseIncompleteEvent, + ResponseInput, + ResponseInputAudio, + ResponseInputContent, + ResponseInputFile, + ResponseInputFileContent, + ResponseInputImage, + ResponseInputImageContent, + ResponseInputItem, + ResponseInputMessageContentList, + ResponseInputMessageItem, + ResponseInputText, + ResponseInputTextContent, + ResponseItem, + ResponseLocalEnvironment, + ResponseMcpCallArgumentsDeltaEvent, + ResponseMcpCallArgumentsDoneEvent, + ResponseMcpCallCompletedEvent, + ResponseMcpCallFailedEvent, + ResponseMcpCallInProgressEvent, + ResponseMcpListToolsCompletedEvent, + ResponseMcpListToolsFailedEvent, + ResponseMcpListToolsInProgressEvent, + ResponseOutputAudio, + ResponseOutputItem, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, + ResponseOutputTextAnnotationAddedEvent, + ResponsePrompt, + ResponseQueuedEvent, + ResponseReasoningItem, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseStatus, + ResponseStreamEvent, + ResponseTextConfig, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseUsage, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + SkillReference, + Tool, + ToolChoiceAllowed, + ToolChoiceApplyPatch, + ToolChoiceCustom, + ToolChoiceFunction, + ToolChoiceMcp, + ToolChoiceOptions, + ToolChoiceShell, + ToolChoiceTypes, + WebSearchPreviewTool, + WebSearchTool, +) +``` + +Methods: + +- client.responses.create(\*\*params) -> Response +- client.responses.retrieve(response_id, \*\*params) -> Response +- client.responses.delete(response_id) -> None +- client.responses.cancel(response_id) -> Response +- client.responses.compact(\*\*params) -> CompactedResponse + +## InputItems + +Types: + +```python +from openai.types.responses import ResponseItemList +``` + +Methods: + +- client.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[ResponseItem] + +## InputTokens + +Types: + +```python +from openai.types.responses import InputTokenCountResponse +``` + +Methods: + +- client.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse diff --git a/src/openai/resources/webhooks/__init__.py b/src/openai/resources/webhooks/__init__.py new file mode 100644 index 0000000000..ba241ddffc --- /dev/null +++ b/src/openai/resources/webhooks/__init__.py @@ -0,0 +1,5 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .webhooks import Webhooks, AsyncWebhooks + +__all__ = ["Webhooks", "AsyncWebhooks"] diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md new file mode 100644 index 0000000000..8e3c312eb0 --- /dev/null +++ b/src/openai/resources/webhooks/api.md @@ -0,0 +1,24 @@ +# Webhooks + +Types: + +```python +from openai.types.webhooks import ( + BatchCancelledWebhookEvent, + BatchCompletedWebhookEvent, + BatchExpiredWebhookEvent, + BatchFailedWebhookEvent, + EvalRunCanceledWebhookEvent, + EvalRunFailedWebhookEvent, + EvalRunSucceededWebhookEvent, + FineTuningJobCancelledWebhookEvent, + FineTuningJobFailedWebhookEvent, + FineTuningJobSucceededWebhookEvent, + RealtimeCallIncomingWebhookEvent, + ResponseCancelledWebhookEvent, + ResponseCompletedWebhookEvent, + ResponseFailedWebhookEvent, + ResponseIncompleteWebhookEvent, + UnwrapWebhookEvent, +) +``` diff --git a/src/openai/resources/webhooks.py b/src/openai/resources/webhooks/webhooks.py similarity index 96% rename from src/openai/resources/webhooks.py rename to src/openai/resources/webhooks/webhooks.py index 3e13d3faae..8d99568aad 100644 --- a/src/openai/resources/webhooks.py +++ b/src/openai/resources/webhooks/webhooks.py @@ -9,12 +9,12 @@ import hashlib from typing import cast -from .._types import HeadersLike -from .._utils import get_required_header -from .._models import construct_type -from .._resource import SyncAPIResource, AsyncAPIResource -from .._exceptions import InvalidWebhookSignatureError -from ..types.webhooks.unwrap_webhook_event import UnwrapWebhookEvent +from ..._types import HeadersLike +from ..._utils import get_required_header +from ..._models import construct_type +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._exceptions import InvalidWebhookSignatureError +from ...types.webhooks.unwrap_webhook_event import UnwrapWebhookEvent __all__ = ["Webhooks", "AsyncWebhooks"] diff --git a/tests/api_resources/webhooks/__init__.py b/tests/api_resources/webhooks/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/webhooks/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. From 3ddbb7ec8dde5ac28e37dcfff95477b9eb48be05 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:13:18 +0000 Subject: [PATCH 238/408] feat(api): container network_policy and skills --- .stats.yml | 4 +-- src/openai/resources/containers/containers.py | 25 +++++++++++++++++++ .../resources/containers/files/files.py | 18 +++++++------ src/openai/types/container_create_params.py | 20 +++++++++++++-- src/openai/types/container_create_response.py | 17 +++++++++++-- src/openai/types/container_list_params.py | 3 +++ src/openai/types/container_list_response.py | 17 +++++++++++-- .../types/container_retrieve_response.py | 17 +++++++++++-- tests/api_resources/test_containers.py | 18 +++++++++++++ 9 files changed, 121 insertions(+), 18 deletions(-) diff --git a/.stats.yml b/.stats.yml index dcc9c27df3..1d0140eb64 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-1595c7e4e1d7bf7eae2e49ad800316e13b69ffb18bff2a2f55236fce792deb53.yml -openapi_spec_hash: c3a3a0d8d4bbf11179149244f017a6dc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0db5326a0fb6a30ffad9242c72872c3388ef927e8a4549ddd20aec3420541209.yml +openapi_spec_hash: 9523fe30739802e15c88f4e7aac44e7f config_hash: 948733484caf41e71093c6582dbc319c diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py index 0cbb400d4a..216097d9c8 100644 --- a/src/openai/resources/containers/containers.py +++ b/src/openai/resources/containers/containers.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Iterable from typing_extensions import Literal import httpx @@ -61,6 +62,8 @@ def create( expires_after: container_create_params.ExpiresAfter | Omit = omit, file_ids: SequenceNotStr[str] | Omit = omit, memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit, + network_policy: container_create_params.NetworkPolicy | Omit = omit, + skills: Iterable[container_create_params.Skill] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -80,6 +83,10 @@ def create( memory_limit: Optional memory limit for the container. Defaults to "1g". + network_policy: Network access policy for the container. + + skills: An optional list of skills referenced by id or inline data. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -96,6 +103,8 @@ def create( "expires_after": expires_after, "file_ids": file_ids, "memory_limit": memory_limit, + "network_policy": network_policy, + "skills": skills, }, container_create_params.ContainerCreateParams, ), @@ -143,6 +152,7 @@ def list( *, after: str | Omit = omit, limit: int | Omit = omit, + name: str | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -164,6 +174,8 @@ def list( limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. + name: Filter results by container name. + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order. @@ -187,6 +199,7 @@ def list( { "after": after, "limit": limit, + "name": name, "order": order, }, container_list_params.ContainerListParams, @@ -261,6 +274,8 @@ async def create( expires_after: container_create_params.ExpiresAfter | Omit = omit, file_ids: SequenceNotStr[str] | Omit = omit, memory_limit: Literal["1g", "4g", "16g", "64g"] | Omit = omit, + network_policy: container_create_params.NetworkPolicy | Omit = omit, + skills: Iterable[container_create_params.Skill] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -280,6 +295,10 @@ async def create( memory_limit: Optional memory limit for the container. Defaults to "1g". + network_policy: Network access policy for the container. + + skills: An optional list of skills referenced by id or inline data. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -296,6 +315,8 @@ async def create( "expires_after": expires_after, "file_ids": file_ids, "memory_limit": memory_limit, + "network_policy": network_policy, + "skills": skills, }, container_create_params.ContainerCreateParams, ), @@ -343,6 +364,7 @@ def list( *, after: str | Omit = omit, limit: int | Omit = omit, + name: str | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -364,6 +386,8 @@ def list( limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. + name: Filter results by container name. + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order. @@ -387,6 +411,7 @@ def list( { "after": after, "limit": limit, + "name": name, "order": order, }, container_list_params.ContainerListParams, diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py index a472cfc9f3..62659a5c3d 100644 --- a/src/openai/resources/containers/files/files.py +++ b/src/openai/resources/containers/files/files.py @@ -96,10 +96,11 @@ def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( f"/containers/{container_id}/files", body=maybe_transform(body, file_create_params.FileCreateParams), @@ -309,10 +310,11 @@ async def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + if files: + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( f"/containers/{container_id}/files", body=await async_maybe_transform(body, file_create_params.FileCreateParams), diff --git a/src/openai/types/container_create_params.py b/src/openai/types/container_create_params.py index 47101ecdb6..63d28f3955 100644 --- a/src/openai/types/container_create_params.py +++ b/src/openai/types/container_create_params.py @@ -2,11 +2,16 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .._types import SequenceNotStr +from .responses.inline_skill_param import InlineSkillParam +from .responses.skill_reference_param import SkillReferenceParam +from .responses.container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam +from .responses.container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam -__all__ = ["ContainerCreateParams", "ExpiresAfter"] +__all__ = ["ContainerCreateParams", "ExpiresAfter", "NetworkPolicy", "Skill"] class ContainerCreateParams(TypedDict, total=False): @@ -22,6 +27,12 @@ class ContainerCreateParams(TypedDict, total=False): memory_limit: Literal["1g", "4g", "16g", "64g"] """Optional memory limit for the container. Defaults to "1g".""" + network_policy: NetworkPolicy + """Network access policy for the container.""" + + skills: Iterable[Skill] + """An optional list of skills referenced by id or inline data.""" + class ExpiresAfter(TypedDict, total=False): """Container expiration time in seconds relative to the 'anchor' time.""" @@ -33,3 +44,8 @@ class ExpiresAfter(TypedDict, total=False): """ minutes: Required[int] + + +NetworkPolicy: TypeAlias = Union[ContainerNetworkPolicyDisabledParam, ContainerNetworkPolicyAllowlistParam] + +Skill: TypeAlias = Union[SkillReferenceParam, InlineSkillParam] diff --git a/src/openai/types/container_create_response.py b/src/openai/types/container_create_response.py index 0ebcc04062..34bc56ad13 100644 --- a/src/openai/types/container_create_response.py +++ b/src/openai/types/container_create_response.py @@ -1,11 +1,11 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from .._models import BaseModel -__all__ = ["ContainerCreateResponse", "ExpiresAfter"] +__all__ = ["ContainerCreateResponse", "ExpiresAfter", "NetworkPolicy"] class ExpiresAfter(BaseModel): @@ -22,6 +22,16 @@ class ExpiresAfter(BaseModel): """The number of minutes after the anchor before the container expires.""" +class NetworkPolicy(BaseModel): + """Network access policy for the container.""" + + type: Literal["allowlist", "disabled"] + """The network policy mode.""" + + allowed_domains: Optional[List[str]] = None + """Allowed outbound domains when `type` is `allowlist`.""" + + class ContainerCreateResponse(BaseModel): id: str """Unique identifier for the container.""" @@ -50,3 +60,6 @@ class ContainerCreateResponse(BaseModel): memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None """The memory limit configured for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" diff --git a/src/openai/types/container_list_params.py b/src/openai/types/container_list_params.py index 4821a87d18..01ec43af32 100644 --- a/src/openai/types/container_list_params.py +++ b/src/openai/types/container_list_params.py @@ -23,6 +23,9 @@ class ContainerListParams(TypedDict, total=False): Limit can range between 1 and 100, and the default is 20. """ + name: str + """Filter results by container name.""" + order: Literal["asc", "desc"] """Sort order by the `created_at` timestamp of the objects. diff --git a/src/openai/types/container_list_response.py b/src/openai/types/container_list_response.py index 8f39548201..bf572acd6b 100644 --- a/src/openai/types/container_list_response.py +++ b/src/openai/types/container_list_response.py @@ -1,11 +1,11 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from .._models import BaseModel -__all__ = ["ContainerListResponse", "ExpiresAfter"] +__all__ = ["ContainerListResponse", "ExpiresAfter", "NetworkPolicy"] class ExpiresAfter(BaseModel): @@ -22,6 +22,16 @@ class ExpiresAfter(BaseModel): """The number of minutes after the anchor before the container expires.""" +class NetworkPolicy(BaseModel): + """Network access policy for the container.""" + + type: Literal["allowlist", "disabled"] + """The network policy mode.""" + + allowed_domains: Optional[List[str]] = None + """Allowed outbound domains when `type` is `allowlist`.""" + + class ContainerListResponse(BaseModel): id: str """Unique identifier for the container.""" @@ -50,3 +60,6 @@ class ContainerListResponse(BaseModel): memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None """The memory limit configured for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" diff --git a/src/openai/types/container_retrieve_response.py b/src/openai/types/container_retrieve_response.py index 9ba3e18c3a..b5a6d350ff 100644 --- a/src/openai/types/container_retrieve_response.py +++ b/src/openai/types/container_retrieve_response.py @@ -1,11 +1,11 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from .._models import BaseModel -__all__ = ["ContainerRetrieveResponse", "ExpiresAfter"] +__all__ = ["ContainerRetrieveResponse", "ExpiresAfter", "NetworkPolicy"] class ExpiresAfter(BaseModel): @@ -22,6 +22,16 @@ class ExpiresAfter(BaseModel): """The number of minutes after the anchor before the container expires.""" +class NetworkPolicy(BaseModel): + """Network access policy for the container.""" + + type: Literal["allowlist", "disabled"] + """The network policy mode.""" + + allowed_domains: Optional[List[str]] = None + """Allowed outbound domains when `type` is `allowlist`.""" + + class ContainerRetrieveResponse(BaseModel): id: str """Unique identifier for the container.""" @@ -50,3 +60,6 @@ class ContainerRetrieveResponse(BaseModel): memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None """The memory limit configured for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" diff --git a/tests/api_resources/test_containers.py b/tests/api_resources/test_containers.py index cf173c7fd5..321d7778b0 100644 --- a/tests/api_resources/test_containers.py +++ b/tests/api_resources/test_containers.py @@ -39,6 +39,14 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: }, file_ids=["string"], memory_limit="1g", + network_policy={"type": "disabled"}, + skills=[ + { + "skill_id": "x", + "type": "skill_reference", + "version": "version", + } + ], ) assert_matches_type(ContainerCreateResponse, container, path=["response"]) @@ -114,6 +122,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: container = client.containers.list( after="after", limit=0, + name="name", order="asc", ) assert_matches_type(SyncCursorPage[ContainerListResponse], container, path=["response"]) @@ -199,6 +208,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> }, file_ids=["string"], memory_limit="1g", + network_policy={"type": "disabled"}, + skills=[ + { + "skill_id": "x", + "type": "skill_reference", + "version": "version", + } + ], ) assert_matches_type(ContainerCreateResponse, container, path=["response"]) @@ -274,6 +291,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N container = await async_client.containers.list( after="after", limit=0, + name="name", order="asc", ) assert_matches_type(AsyncCursorPage[ContainerListResponse], container, path=["response"]) From e93f6cff4f542388409c25bf2fbb5e7ce28fec9c Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Fri, 13 Feb 2026 18:46:01 -0500 Subject: [PATCH 239/408] fix(webhooks): preserve method visibility for compatibility checks --- src/openai/resources/webhooks/__init__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/openai/resources/webhooks/__init__.py b/src/openai/resources/webhooks/__init__.py index ba241ddffc..371906299b 100644 --- a/src/openai/resources/webhooks/__init__.py +++ b/src/openai/resources/webhooks/__init__.py @@ -1,5 +1,13 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .webhooks import Webhooks, AsyncWebhooks +from .webhooks import Webhooks as _Webhooks, AsyncWebhooks as _AsyncWebhooks + + +class Webhooks(_Webhooks): + pass + + +class AsyncWebhooks(_AsyncWebhooks): + pass __all__ = ["Webhooks", "AsyncWebhooks"] From 3e0c05b84a2056870abf3bd6a5e7849020209cc3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 23:46:41 +0000 Subject: [PATCH 240/408] release: 2.21.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 25 +++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 26b25a25e8..49df20d2b8 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.20.0" + ".": "2.21.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 39ed325e8e..6f558b3626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 2.21.0 (2026-02-13) + +Full Changelog: [v2.20.0...v2.21.0](https://github.com/openai/openai-python/compare/v2.20.0...v2.21.0) + +### Features + +* **api:** container network_policy and skills ([d19de2e](https://github.com/openai/openai-python/commit/d19de2ee5c74413f9dc52684b650df1898dee82b)) + + +### Bug Fixes + +* **structured outputs:** resolve memory leak in parse methods ([#2860](https://github.com/openai/openai-python/issues/2860)) ([6dcbe21](https://github.com/openai/openai-python/commit/6dcbe211f12f8470db542a5cb95724cb933786dd)) +* **webhooks:** preserve method visibility for compatibility checks ([44a8936](https://github.com/openai/openai-python/commit/44a8936d580b770f23fae79659101a27eadafad6)) + + +### Chores + +* **internal:** fix lint error on Python 3.14 ([534f215](https://github.com/openai/openai-python/commit/534f215941f504443d63509e872409a0b1236452)) + + +### Documentation + +* split `api.md` by standalone resources ([96e41b3](https://github.com/openai/openai-python/commit/96e41b398a110212ddec71436b2439343bea87d4)) +* update comment ([63def23](https://github.com/openai/openai-python/commit/63def23b7acd5c6dacf03337fe1bd08439d1dba8)) + ## 2.20.0 (2026-02-10) Full Changelog: [v2.19.0...v2.20.0](https://github.com/openai/openai-python/compare/v2.19.0...v2.20.0) diff --git a/pyproject.toml b/pyproject.toml index ed8ee459cd..fe2e394592 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.20.0" +version = "2.21.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 4987d1ca18..0d4ef7b71c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.20.0" # x-release-please-version +__version__ = "2.21.0" # x-release-please-version From 5047206f02c0470fbabac705e91bae9cb0911c52 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 02:31:27 +0000 Subject: [PATCH 241/408] docs(api): enhance method descriptions across audio, chat, realtime, skills, uploads, videos --- .stats.yml | 4 +- src/openai/resources/audio/speech.py | 4 ++ src/openai/resources/audio/transcriptions.py | 18 +++++++++ src/openai/resources/beta/chatkit/sessions.py | 12 ++++-- src/openai/resources/beta/chatkit/threads.py | 16 ++++---- .../resources/chat/completions/completions.py | 18 +++++++++ src/openai/resources/completions.py | 18 +++++++++ .../resources/realtime/client_secrets.py | 28 ++++++++++++++ .../resources/responses/input_tokens.py | 10 ++++- src/openai/resources/responses/responses.py | 20 ++++++++-- src/openai/resources/skills/content.py | 4 +- src/openai/resources/skills/skills.py | 20 +++++----- .../resources/skills/versions/content.py | 4 +- .../resources/skills/versions/versions.py | 16 ++++---- src/openai/resources/uploads/uploads.py | 16 +++++++- src/openai/resources/videos.py | 38 ++++++++++--------- 16 files changed, 184 insertions(+), 62 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1d0140eb64..0ab1454b8b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0db5326a0fb6a30ffad9242c72872c3388ef927e8a4549ddd20aec3420541209.yml -openapi_spec_hash: 9523fe30739802e15c88f4e7aac44e7f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c20486f46004d6be2d280d7792c64d47fcea3e5b7fbbb50d3ffc6241aba653df.yml +openapi_spec_hash: bf1dbabc5a923d897309273183525c02 config_hash: 948733484caf41e71093c6582dbc319c diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index f2c8d635f3..96a32f9268 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -67,6 +67,8 @@ def create( """ Generates audio from the input text. + Returns the audio file content, or a stream of audio events. + Args: input: The text to generate audio for. The maximum length is 4096 characters. @@ -164,6 +166,8 @@ async def create( """ Generates audio from the input text. + Returns the audio file content, or a stream of audio events. + Args: input: The text to generate audio for. The maximum length is 4096 characters. diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 599534855d..bc6e9f22de 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -85,6 +85,9 @@ def create( """ Transcribes audio into the input language. + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, or a stream of transcript events. + Args: file: The audio file object (not file name) to transcribe, in one of these formats: @@ -235,6 +238,9 @@ def create( """ Transcribes audio into the input language. + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, or a stream of transcript events. + Args: file: The audio file object (not file name) to transcribe, in one of these formats: @@ -343,6 +349,9 @@ def create( """ Transcribes audio into the input language. + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, or a stream of transcript events. + Args: file: The audio file object (not file name) to transcribe, in one of these formats: @@ -533,6 +542,9 @@ async def create( """ Transcribes audio into the input language. + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, or a stream of transcript events. + Args: file: The audio file object (not file name) to transcribe, in one of these formats: @@ -678,6 +690,9 @@ async def create( """ Transcribes audio into the input language. + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, or a stream of transcript events. + Args: file: The audio file object (not file name) to transcribe, in one of these formats: @@ -786,6 +801,9 @@ async def create( """ Transcribes audio into the input language. + Returns a transcription object in `json`, `diarized_json`, or `verbose_json` + format, or a stream of transcript events. + Args: file: The audio file object (not file name) to transcribe, in one of these formats: diff --git a/src/openai/resources/beta/chatkit/sessions.py b/src/openai/resources/beta/chatkit/sessions.py index a814f1058e..abfa496a56 100644 --- a/src/openai/resources/beta/chatkit/sessions.py +++ b/src/openai/resources/beta/chatkit/sessions.py @@ -63,7 +63,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatSession: """ - Create a ChatKit session + Create a ChatKit session. Args: user: A free-form string that identifies your end user; ensures this Session can @@ -117,7 +117,9 @@ def cancel( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatSession: """ - Cancel a ChatKit session + Cancel an active ChatKit session and return its most recent metadata. + + Cancelling prevents new requests from using the issued client secret. Args: extra_headers: Send extra headers @@ -176,7 +178,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatSession: """ - Create a ChatKit session + Create a ChatKit session. Args: user: A free-form string that identifies your end user; ensures this Session can @@ -230,7 +232,9 @@ async def cancel( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatSession: """ - Cancel a ChatKit session + Cancel an active ChatKit session and return its most recent metadata. + + Cancelling prevents new requests from using the issued client secret. Args: extra_headers: Send extra headers diff --git a/src/openai/resources/beta/chatkit/threads.py b/src/openai/resources/beta/chatkit/threads.py index 37cd57295a..7a2d4c4a30 100644 --- a/src/openai/resources/beta/chatkit/threads.py +++ b/src/openai/resources/beta/chatkit/threads.py @@ -55,7 +55,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatKitThread: """ - Retrieve a ChatKit thread + Retrieve a ChatKit thread by its identifier. Args: extra_headers: Send extra headers @@ -93,7 +93,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncConversationCursorPage[ChatKitThread]: """ - List ChatKit threads + List ChatKit threads with optional pagination and user filters. Args: after: List items created after this thread item ID. Defaults to null for the first @@ -152,7 +152,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ThreadDeleteResponse: """ - Delete a ChatKit thread + Delete a ChatKit thread along with its items and stored attachments. Args: extra_headers: Send extra headers @@ -190,7 +190,7 @@ def list_items( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncConversationCursorPage[Data]: """ - List ChatKit thread items + List items that belong to a ChatKit thread. Args: after: List items created after this thread item ID. Defaults to null for the first @@ -268,7 +268,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChatKitThread: """ - Retrieve a ChatKit thread + Retrieve a ChatKit thread by its identifier. Args: extra_headers: Send extra headers @@ -306,7 +306,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ChatKitThread, AsyncConversationCursorPage[ChatKitThread]]: """ - List ChatKit threads + List ChatKit threads with optional pagination and user filters. Args: after: List items created after this thread item ID. Defaults to null for the first @@ -365,7 +365,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ThreadDeleteResponse: """ - Delete a ChatKit thread + Delete a ChatKit thread along with its items and stored attachments. Args: extra_headers: Send extra headers @@ -403,7 +403,7 @@ def list_items( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Data, AsyncConversationCursorPage[Data]]: """ - List ChatKit thread items + List items that belong to a ChatKit thread. Args: after: List items created after this thread item ID. Defaults to null for the first diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index fb1887a7d5..e1fc531fa9 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -301,6 +301,9 @@ def create( unsupported parameters in reasoning models, [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -603,6 +606,9 @@ def create( unsupported parameters in reasoning models, [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -905,6 +911,9 @@ def create( unsupported parameters in reasoning models, [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -1785,6 +1794,9 @@ async def create( unsupported parameters in reasoning models, [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -2087,6 +2099,9 @@ async def create( unsupported parameters in reasoning models, [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message @@ -2389,6 +2404,9 @@ async def create( unsupported parameters in reasoning models, [refer to the reasoning guide](https://platform.openai.com/docs/guides/reasoning). + Returns a chat completion object, or a streamed sequence of chat completion + chunk objects if the request is streamed. + Args: messages: A list of messages comprising the conversation so far. Depending on the [model](https://platform.openai.com/docs/models) you use, different message diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 2f2284a622..4b6e29395b 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -76,6 +76,9 @@ def create( """ Creates a completion for the provided prompt and parameters. + Returns a completion object, or a sequence of completion objects if the request + is streamed. + Args: model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to @@ -231,6 +234,9 @@ def create( """ Creates a completion for the provided prompt and parameters. + Returns a completion object, or a sequence of completion objects if the request + is streamed. + Args: model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to @@ -386,6 +392,9 @@ def create( """ Creates a completion for the provided prompt and parameters. + Returns a completion object, or a sequence of completion objects if the request + is streamed. + Args: model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to @@ -626,6 +635,9 @@ async def create( """ Creates a completion for the provided prompt and parameters. + Returns a completion object, or a sequence of completion objects if the request + is streamed. + Args: model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to @@ -781,6 +793,9 @@ async def create( """ Creates a completion for the provided prompt and parameters. + Returns a completion object, or a sequence of completion objects if the request + is streamed. + Args: model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to @@ -936,6 +951,9 @@ async def create( """ Creates a completion for the provided prompt and parameters. + Returns a completion object, or a sequence of completion objects if the request + is streamed. + Args: model: ID of the model to use. You can use the [List models](https://platform.openai.com/docs/api-reference/models/list) API to diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py index 5ceba7bef1..d9947dd7e8 100644 --- a/src/openai/resources/realtime/client_secrets.py +++ b/src/openai/resources/realtime/client_secrets.py @@ -52,6 +52,20 @@ def create( """ Create a Realtime client secret with an associated session configuration. + Client secrets are short-lived tokens that can be passed to a client app, such + as a web frontend or mobile client, which grants access to the Realtime API + without leaking your main API key. You can configure a custom TTL for each + client secret. + + You can also attach session configuration options to the client secret, which + will be applied to any sessions created using that client secret, but these can + also be overridden by the client connection. + + [Learn more about authentication with client secrets over WebRTC](https://platform.openai.com/docs/guides/realtime-webrtc). + + Returns the created client secret and the effective session object. The client + secret is a string that looks like `ek_1234`. + Args: expires_after: Configuration for the client secret expiration. Expiration refers to the time after which a client secret will no longer be valid for creating sessions. The @@ -120,6 +134,20 @@ async def create( """ Create a Realtime client secret with an associated session configuration. + Client secrets are short-lived tokens that can be passed to a client app, such + as a web frontend or mobile client, which grants access to the Realtime API + without leaking your main API key. You can configure a custom TTL for each + client secret. + + You can also attach session configuration options to the client secret, which + will be applied to any sessions created using that client secret, but these can + also be overridden by the client connection. + + [Learn more about authentication with client secrets over WebRTC](https://platform.openai.com/docs/guides/realtime-webrtc). + + Returns the created client secret and the effective session object. The client + secret is a string that looks like `ek_1234`. + Args: expires_after: Configuration for the client secret expiration. Expiration refers to the time after which a client secret will no longer be valid for creating sessions. The diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py index 8664164655..0056727fa0 100644 --- a/src/openai/resources/responses/input_tokens.py +++ b/src/openai/resources/responses/input_tokens.py @@ -65,7 +65,10 @@ def count( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> InputTokenCountResponse: """ - Get input token counts + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. Args: conversation: The conversation that this response belongs to. Items from this conversation are @@ -188,7 +191,10 @@ async def count( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> InputTokenCountResponse: """ - Get input token counts + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. Args: conversation: The conversation that this response belongs to. Items from this conversation are diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 79034b7e18..4cb0e35cd3 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1645,8 +1645,14 @@ def compact( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CompactedResponse: - """ - Compact conversation + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced). Args: model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a @@ -3280,8 +3286,14 @@ async def compact( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CompactedResponse: - """ - Compact conversation + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced). Args: model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a diff --git a/src/openai/resources/skills/content.py b/src/openai/resources/skills/content.py index 98c1531a94..c912fd3eb3 100644 --- a/src/openai/resources/skills/content.py +++ b/src/openai/resources/skills/content.py @@ -51,7 +51,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ - Get Skill Content + Download a skill zip bundle by its ID. Args: extra_headers: Send extra headers @@ -106,7 +106,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ - Get Skill Content + Download a skill zip bundle by its ID. Args: extra_headers: Send extra headers diff --git a/src/openai/resources/skills/skills.py b/src/openai/resources/skills/skills.py index b0e929bccf..77bed029df 100644 --- a/src/openai/resources/skills/skills.py +++ b/src/openai/resources/skills/skills.py @@ -88,7 +88,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Skill: """ - Create Skill + Create a new skill. Args: files: Skill files to upload (directory upload) or a single zip file. @@ -130,7 +130,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Skill: """ - Get Skill + Get a skill by its ID. Args: extra_headers: Send extra headers @@ -164,7 +164,7 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Skill: """ - Update Skill Default Version + Update the default version pointer for a skill. Args: default_version: The skill version number to set as default. @@ -202,7 +202,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[Skill]: """ - List Skills + List all skills for the current project. Args: after: Identifier for the last item from the previous pagination request @@ -252,7 +252,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedSkill: """ - Delete Skill + Delete a skill by its ID. Args: extra_headers: Send extra headers @@ -314,7 +314,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Skill: """ - Create Skill + Create a new skill. Args: files: Skill files to upload (directory upload) or a single zip file. @@ -356,7 +356,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Skill: """ - Get Skill + Get a skill by its ID. Args: extra_headers: Send extra headers @@ -390,7 +390,7 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Skill: """ - Update Skill Default Version + Update the default version pointer for a skill. Args: default_version: The skill version number to set as default. @@ -430,7 +430,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Skill, AsyncCursorPage[Skill]]: """ - List Skills + List all skills for the current project. Args: after: Identifier for the last item from the previous pagination request @@ -480,7 +480,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedSkill: """ - Delete Skill + Delete a skill by its ID. Args: extra_headers: Send extra headers diff --git a/src/openai/resources/skills/versions/content.py b/src/openai/resources/skills/versions/content.py index 4494ca0e2f..182a563dde 100644 --- a/src/openai/resources/skills/versions/content.py +++ b/src/openai/resources/skills/versions/content.py @@ -52,7 +52,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ - Get Skill Version Content + Download a skill version zip bundle. Args: version: The skill version number. @@ -112,7 +112,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: """ - Get Skill Version Content + Download a skill version zip bundle. Args: version: The skill version number. diff --git a/src/openai/resources/skills/versions/versions.py b/src/openai/resources/skills/versions/versions.py index 890a20774e..610a24240a 100644 --- a/src/openai/resources/skills/versions/versions.py +++ b/src/openai/resources/skills/versions/versions.py @@ -78,7 +78,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillVersion: """ - Create Skill Version + Create a new immutable skill version. Args: default: Whether to set this version as the default. @@ -130,7 +130,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillVersion: """ - Get Skill Version + Get a specific skill version. Args: version: The version number to retrieve. @@ -170,7 +170,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[SkillVersion]: """ - List Skill Versions + List skill versions for a skill. Args: after: The skill version ID to start after. @@ -222,7 +222,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedSkillVersion: """ - Delete Skill Version + Delete a skill version. Args: version: The skill version number. @@ -286,7 +286,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillVersion: """ - Create Skill Version + Create a new immutable skill version. Args: default: Whether to set this version as the default. @@ -338,7 +338,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillVersion: """ - Get Skill Version + Get a specific skill version. Args: version: The version number to retrieve. @@ -378,7 +378,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[SkillVersion, AsyncCursorPage[SkillVersion]]: """ - List Skill Versions + List skill versions for a skill. Args: after: The skill version ID to start after. @@ -430,7 +430,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedSkillVersion: """ - Delete Skill Version + Delete a skill version. Args: version: The skill version number. diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index e8c047bd4f..2873b913ba 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -198,6 +198,8 @@ def create( the documentation on [creating a File](https://platform.openai.com/docs/api-reference/files/create). + Returns the Upload object with status `pending`. + Args: bytes: The number of bytes in the file you are uploading. @@ -257,6 +259,8 @@ def cancel( No Parts may be added after an Upload is cancelled. + Returns the Upload object with status `cancelled`. + Args: extra_headers: Send extra headers @@ -302,7 +306,9 @@ def complete( The number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after - an Upload is completed. + an Upload is completed. Returns the Upload object with status `completed`, + including an additional `file` property containing the created usable File + object. Args: part_ids: The ordered list of Part IDs. @@ -505,6 +511,8 @@ async def create( the documentation on [creating a File](https://platform.openai.com/docs/api-reference/files/create). + Returns the Upload object with status `pending`. + Args: bytes: The number of bytes in the file you are uploading. @@ -564,6 +572,8 @@ async def cancel( No Parts may be added after an Upload is cancelled. + Returns the Upload object with status `cancelled`. + Args: extra_headers: Send extra headers @@ -609,7 +619,9 @@ async def complete( The number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after - an Upload is completed. + an Upload is completed. Returns the Upload object with status `completed`, + including an additional `file` property containing the created usable File + object. Args: part_ids: The ordered list of Part IDs. diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 9f74c942bc..85ea79f8bc 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -76,7 +76,7 @@ def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Video: """ - Create a video + Create a new video generation job from a prompt and optional reference assets. Args: prompt: Text prompt that describes the video to generate. @@ -209,7 +209,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Video: """ - Retrieve a video + Fetch the latest metadata for a generated video. Args: extra_headers: Send extra headers @@ -244,7 +244,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncConversationCursorPage[Video]: """ - List videos + List recently generated videos for the current project. Args: after: Identifier for the last item from the previous pagination request @@ -294,7 +294,7 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VideoDeleteResponse: """ - Delete a video + Permanently delete a completed or failed video and its stored assets. Args: extra_headers: Send extra headers @@ -327,12 +327,13 @@ def download_content( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: - """Download video content + """ + Download the generated video bytes or a derived preview asset. - Args: - variant: Which downloadable asset to return. + Streams the rendered video content for the specified video job. - Defaults to the MP4 video. + Args: + variant: Which downloadable asset to return. Defaults to the MP4 video. extra_headers: Send extra headers @@ -370,7 +371,7 @@ def remix( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Video: """ - Create a video remix + Create a remix of a completed video using a refreshed prompt. Args: prompt: Updated text prompt that directs the remix generation. @@ -431,7 +432,7 @@ async def create( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Video: """ - Create a video + Create a new video generation job from a prompt and optional reference assets. Args: prompt: Text prompt that describes the video to generate. @@ -564,7 +565,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Video: """ - Retrieve a video + Fetch the latest metadata for a generated video. Args: extra_headers: Send extra headers @@ -599,7 +600,7 @@ def list( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Video, AsyncConversationCursorPage[Video]]: """ - List videos + List recently generated videos for the current project. Args: after: Identifier for the last item from the previous pagination request @@ -649,7 +650,7 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VideoDeleteResponse: """ - Delete a video + Permanently delete a completed or failed video and its stored assets. Args: extra_headers: Send extra headers @@ -682,12 +683,13 @@ async def download_content( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> _legacy_response.HttpxBinaryResponseContent: - """Download video content + """ + Download the generated video bytes or a derived preview asset. - Args: - variant: Which downloadable asset to return. + Streams the rendered video content for the specified video job. - Defaults to the MP4 video. + Args: + variant: Which downloadable asset to return. Defaults to the MP4 video. extra_headers: Send extra headers @@ -727,7 +729,7 @@ async def remix( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Video: """ - Create a video remix + Create a remix of a completed video using a refreshed prompt. Args: prompt: Updated text prompt that directs the remix generation. From d1efdc4abdd93d73994dba69293d5f235010c16c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 02:54:17 +0000 Subject: [PATCH 242/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0ab1454b8b..88bd7bd561 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-c20486f46004d6be2d280d7792c64d47fcea3e5b7fbbb50d3ffc6241aba653df.yml -openapi_spec_hash: bf1dbabc5a923d897309273183525c02 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a6dd5f8d7318fb1c5370d5ccb7148bacdfb3f3a665c4b85b7666e6188d9bd909.yml +openapi_spec_hash: c4824e385a81b9021428304ccc96538f config_hash: 948733484caf41e71093c6582dbc319c From f82957313692ed99e321906780c57d4d99e737af Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:38:04 +0000 Subject: [PATCH 243/408] chore: update mock server docs --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c14e652328..3a1cf70bb8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,8 +88,7 @@ $ pip install ./path-to-wheel-file.whl Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. ```sh -# you will need npm installed -$ npx prism mock path/to/your/openapi.yml +$ ./scripts/mock ``` ```sh From 5e5bc78aa1a8db86876635c786f127f242fdb0f1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:06:42 +0000 Subject: [PATCH 244/408] docs(api): update safety_identifier documentation in chat completions and responses --- .stats.yml | 4 +-- .../resources/chat/completions/completions.py | 30 +++++++++++-------- src/openai/resources/responses/responses.py | 30 +++++++++++-------- .../types/chat/completion_create_params.py | 5 ++-- src/openai/types/responses/response.py | 5 ++-- .../types/responses/response_create_params.py | 5 ++-- 6 files changed, 47 insertions(+), 32 deletions(-) diff --git a/.stats.yml b/.stats.yml index 88bd7bd561..af7cf829be 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a6dd5f8d7318fb1c5370d5ccb7148bacdfb3f3a665c4b85b7666e6188d9bd909.yml -openapi_spec_hash: c4824e385a81b9021428304ccc96538f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0d1a3d48d59d864f24002e6e58a4cf38cb80ba1f4b234f6f766789e2866820c8.yml +openapi_spec_hash: 65fce2adfac759c10dc8bd51e09fa3db config_hash: 948733484caf41e71093c6582dbc319c diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index e1fc531fa9..5d56d05d87 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -439,8 +439,9 @@ def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). seed: This feature is in Beta. If specified, our system will make a best effort to @@ -753,8 +754,9 @@ def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). seed: This feature is in Beta. If specified, our system will make a best effort to @@ -1058,8 +1060,9 @@ def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). seed: This feature is in Beta. If specified, our system will make a best effort to @@ -1932,8 +1935,9 @@ async def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). seed: This feature is in Beta. If specified, our system will make a best effort to @@ -2246,8 +2250,9 @@ async def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). seed: This feature is in Beta. If specified, our system will make a best effort to @@ -2551,8 +2556,9 @@ async def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). seed: This feature is in Beta. If specified, our system will make a best effort to diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 4cb0e35cd3..3e1eb53d88 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -238,8 +238,9 @@ def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). service_tier: Specifies the processing type used for serving the request. @@ -493,8 +494,9 @@ def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). service_tier: Specifies the processing type used for serving the request. @@ -741,8 +743,9 @@ def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). service_tier: Specifies the processing type used for serving the request. @@ -1875,8 +1878,9 @@ async def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). service_tier: Specifies the processing type used for serving the request. @@ -2130,8 +2134,9 @@ async def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). service_tier: Specifies the processing type used for serving the request. @@ -2378,8 +2383,9 @@ async def create( safety_identifier: A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). service_tier: Specifies the processing type used for serving the request. diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 49cefb95fc..8e71ccbe41 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -227,8 +227,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 6bac7d65de..ada0783bce 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -233,8 +233,9 @@ class Response(BaseModel): """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 97aaf9dc3a..0e78d1559b 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -171,8 +171,9 @@ class ResponseCreateParamsBase(TypedDict, total=False): """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely - identifies each user. We recommend hashing their username or email address, in - order to avoid sending us any identifying information. + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). """ From 849c8df45c8d295604031edfbfcf7e5ce4699c5a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 22:06:51 +0000 Subject: [PATCH 245/408] docs(api): add batch size limit to file_batches parameter descriptions --- .stats.yml | 4 ++-- src/openai/resources/vector_stores/file_batches.py | 12 ++++++++---- .../types/vector_stores/file_batch_create_params.py | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index af7cf829be..3c69681b59 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0d1a3d48d59d864f24002e6e58a4cf38cb80ba1f4b234f6f766789e2866820c8.yml -openapi_spec_hash: 65fce2adfac759c10dc8bd51e09fa3db +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-46434d15307c0a84fad213c705ba29ab7342f27aa377e302d506a51cb784613b.yml +openapi_spec_hash: 4a64f88af6142d69d6e01dc9207f232a config_hash: 948733484caf41e71093c6582dbc319c diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index fca1ef89fa..13ffa66d1a 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -79,12 +79,14 @@ def create( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied - to all files in the batch. Mutually exclusive with `files`. + to all files in the batch. The maximum batch size is 2000 files. Mutually + exclusive with `files`. files: A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must - be specified for each file. Mutually exclusive with `file_ids`. + be specified for each file. The maximum batch size is 2000 files. Mutually + exclusive with `file_ids`. extra_headers: Send extra headers @@ -438,12 +440,14 @@ async def create( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied - to all files in the batch. Mutually exclusive with `files`. + to all files in the batch. The maximum batch size is 2000 files. Mutually + exclusive with `files`. files: A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must - be specified for each file. Mutually exclusive with `file_ids`. + be specified for each file. The maximum batch size is 2000 files. Mutually + exclusive with `file_ids`. extra_headers: Send extra headers diff --git a/src/openai/types/vector_stores/file_batch_create_params.py b/src/openai/types/vector_stores/file_batch_create_params.py index 2ab98a83ab..7ca0de81da 100644 --- a/src/openai/types/vector_stores/file_batch_create_params.py +++ b/src/openai/types/vector_stores/file_batch_create_params.py @@ -33,7 +33,8 @@ class FileBatchCreateParams(TypedDict, total=False): A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied - to all files in the batch. Mutually exclusive with `files`. + to all files in the batch. The maximum batch size is 2000 files. Mutually + exclusive with `files`. """ files: Iterable[File] @@ -41,7 +42,8 @@ class FileBatchCreateParams(TypedDict, total=False): A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must - be specified for each file. Mutually exclusive with `file_ids`. + be specified for each file. The maximum batch size is 2000 files. Mutually + exclusive with `file_ids`. """ From c612cfb2bbf8f119839c0625edee523dbf9f54d3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:36:19 +0000 Subject: [PATCH 246/408] chore(internal): add request options to SSE classes --- src/openai/_legacy_response.py | 3 +++ src/openai/_response.py | 3 +++ src/openai/_streaming.py | 11 ++++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index cfabaa2fc2..1a58c2dfc3 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -221,6 +221,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -231,6 +232,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -244,6 +246,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/openai/_response.py b/src/openai/_response.py index 350da38dd4..f286d38e6c 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -152,6 +152,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -162,6 +163,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -175,6 +177,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 61a742668a..86b81c324f 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -4,7 +4,7 @@ import json import inspect from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx @@ -14,6 +14,7 @@ if TYPE_CHECKING: from ._client import OpenAI, AsyncOpenAI + from ._models import FinalRequestOptions _T = TypeVar("_T") @@ -23,7 +24,7 @@ class Stream(Generic[_T]): """Provides the core interface to iterate over a synchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEBytesDecoder def __init__( @@ -32,10 +33,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: OpenAI, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() @@ -125,7 +128,7 @@ class AsyncStream(Generic[_T]): """Provides the core interface to iterate over an asynchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEDecoder | SSEBytesDecoder def __init__( @@ -134,10 +137,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: AsyncOpenAI, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() From e273d622e85d7380f5ce715172c23d55ce8625a9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:56:46 +0000 Subject: [PATCH 247/408] feat(api): websockets for responses api --- .stats.yml | 6 +- examples/responses/websocket.py | 436 ++++++++++++++ src/openai/resources/responses/api.md | 2 + src/openai/resources/responses/responses.py | 547 +++++++++++++++++- src/openai/types/responses/__init__.py | 5 + .../responses/input_token_count_params.py | 4 +- .../responses/response_conversation_param.py | 8 +- .../response_conversation_param_param.py | 14 + .../types/responses/response_create_params.py | 4 +- src/openai/types/responses/response_input.py | 10 + .../types/responses/responses_client_event.py | 326 +++++++++++ .../responses/responses_client_event_param.py | 327 +++++++++++ .../types/responses/responses_server_event.py | 120 ++++ 13 files changed, 1794 insertions(+), 15 deletions(-) create mode 100644 examples/responses/websocket.py create mode 100644 src/openai/types/responses/response_conversation_param_param.py create mode 100644 src/openai/types/responses/response_input.py create mode 100644 src/openai/types/responses/responses_client_event.py create mode 100644 src/openai/types/responses/responses_client_event_param.py create mode 100644 src/openai/types/responses/responses_server_event.py diff --git a/.stats.yml b/.stats.yml index 3c69681b59..6d954e3ef8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-46434d15307c0a84fad213c705ba29ab7342f27aa377e302d506a51cb784613b.yml -openapi_spec_hash: 4a64f88af6142d69d6e01dc9207f232a -config_hash: 948733484caf41e71093c6582dbc319c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-95886b357a553078e7f15505fe1c518c7daf11506946049c75eadf89d44da863.yml +openapi_spec_hash: 8dfdf1e1d1dbe58b236b89203aa2a1b0 +config_hash: 4c2841519fd72fe44c18de4c18db231f diff --git a/examples/responses/websocket.py b/examples/responses/websocket.py new file mode 100644 index 0000000000..2c51d8ef02 --- /dev/null +++ b/examples/responses/websocket.py @@ -0,0 +1,436 @@ +from __future__ import annotations + +import json +import argparse +from typing import TYPE_CHECKING, Dict, Union, Literal, Optional, TypedDict, NamedTuple, cast + +from openai import OpenAI +from openai.types.responses import ( + FunctionToolParam, + ToolChoiceOptions, + ResponseInputParam, + ResponseFailedEvent, + ResponseCompletedEvent, + ResponseInputItemParam, + ResponseIncompleteEvent, + ToolChoiceFunctionParam, +) + +if TYPE_CHECKING: + from openai.resources.responses.responses import ResponsesConnection + +ToolName = Literal["get_sku_inventory", "get_supplier_eta", "get_quality_alerts"] +ToolChoice = Union[ToolChoiceOptions, ToolChoiceFunctionParam] + + +class DemoTurn(TypedDict): + tool_name: ToolName + prompt: str + + +class SKUArguments(TypedDict): + sku: str + + +class SKUInventoryOutput(TypedDict): + sku: str + warehouse: str + on_hand_units: int + reserved_units: int + reorder_point: int + safety_stock: int + + +class SupplierShipment(TypedDict): + shipment_id: str + eta_date: str + quantity: int + risk: str + + +class SupplierETAOutput(TypedDict): + sku: str + supplier_shipments: list[SupplierShipment] + + +class QualityAlert(TypedDict): + alert_id: str + status: str + severity: str + summary: str + + +class QualityAlertsOutput(TypedDict): + sku: str + alerts: list[QualityAlert] + + +class FunctionCallOutputItem(TypedDict): + type: Literal["function_call_output"] + call_id: str + output: str + + +class FunctionCallRequest(NamedTuple): + name: str + arguments_json: str + call_id: str + + +class RunResponseResult(NamedTuple): + text: str + response_id: str + function_calls: list[FunctionCallRequest] + + +class RunTurnResult(NamedTuple): + assistant_text: str + response_id: str + + +ToolOutput = Union[SKUInventoryOutput, SupplierETAOutput, QualityAlertsOutput] + +TOOLS: list[FunctionToolParam] = [ + { + "type": "function", + "name": "get_sku_inventory", + "description": "Return froge pond inventory details for a SKU.", + "strict": True, + "parameters": { + "type": "object", + "properties": { + "sku": { + "type": "string", + "description": "Stock-keeping unit identifier, such as sku-froge-lily-pad-deluxe.", + } + }, + "required": ["sku"], + "additionalProperties": False, + }, + }, + { + "type": "function", + "name": "get_supplier_eta", + "description": "Return tadpole supplier restock ETA data for a SKU.", + "strict": True, + "parameters": { + "type": "object", + "properties": { + "sku": { + "type": "string", + "description": "Stock-keeping unit identifier, such as sku-froge-lily-pad-deluxe.", + } + }, + "required": ["sku"], + "additionalProperties": False, + }, + }, + { + "type": "function", + "name": "get_quality_alerts", + "description": "Return recent froge quality alerts for a SKU.", + "strict": True, + "parameters": { + "type": "object", + "properties": { + "sku": { + "type": "string", + "description": "Stock-keeping unit identifier, such as sku-froge-lily-pad-deluxe.", + } + }, + "required": ["sku"], + "additionalProperties": False, + }, + }, +] + +DEMO_TURNS: list[DemoTurn] = [ + { + "tool_name": "get_sku_inventory", + "prompt": "Use get_sku_inventory for sku='sku-froge-lily-pad-deluxe' and summarize current pond stock health in one sentence.", + }, + { + "tool_name": "get_supplier_eta", + "prompt": "Now use get_supplier_eta for the same SKU and summarize restock ETA and tadpole shipment risk.", + }, + { + "tool_name": "get_quality_alerts", + "prompt": "Finally use get_quality_alerts for the same SKU and summarize unresolved froge quality concerns in one short paragraph.", + }, +] + +BETA_HEADER_VALUE = "responses_websockets=2026-02-06" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description=("Run a 3-turn Responses WebSocket demo with function calling and chained previous_response_id.") + ) + parser.add_argument("--model", default="gpt-5.2", help="Model used in the `response.create` payload.") + parser.add_argument( + "--use-beta-header", + action="store_true", + help=f"Include `OpenAI-Beta: {BETA_HEADER_VALUE}` for beta websocket behavior.", + ) + parser.add_argument( + "--show-events", + action="store_true", + help="Print non-text event types while streaming.", + ) + parser.add_argument( + "--show-tool-io", + action="store_true", + help="Print each tool call and tool output payload.", + ) + return parser.parse_args() + + +def parse_tool_name(name: str) -> ToolName: + if name not in {"get_sku_inventory", "get_supplier_eta", "get_quality_alerts"}: + raise ValueError(f"Unsupported tool requested: {name}") + return cast(ToolName, name) + + +def parse_sku_arguments(raw_arguments: str) -> SKUArguments: + parsed_raw = json.loads(raw_arguments) + if not isinstance(parsed_raw, dict): + raise ValueError(f"Tool arguments must be a JSON object: {raw_arguments}") + + parsed = cast(Dict[str, object], parsed_raw) + sku_value = parsed.get("sku") + if not isinstance(sku_value, str): + raise ValueError(f"Tool arguments must include a string `sku`: {raw_arguments}") + + return {"sku": sku_value} + + +def call_tool(name: ToolName, arguments: SKUArguments) -> ToolOutput: + sku = arguments["sku"] + + if name == "get_sku_inventory": + return { + "sku": sku, + "warehouse": "pond-west-1", + "on_hand_units": 84, + "reserved_units": 26, + "reorder_point": 60, + "safety_stock": 40, + } + + if name == "get_supplier_eta": + return { + "sku": sku, + "supplier_shipments": [ + { + "shipment_id": "frog_ship_2201", + "eta_date": "2026-02-24", + "quantity": 180, + "risk": "low", + }, + { + "shipment_id": "frog_ship_2205", + "eta_date": "2026-03-03", + "quantity": 220, + "risk": "medium", + }, + ], + } + + if name == "get_quality_alerts": + return { + "sku": sku, + "alerts": [ + { + "alert_id": "frog_qa_781", + "status": "open", + "severity": "high", + "summary": "Lily-pad coating chipping in lot LP-42", + }, + { + "alert_id": "frog_qa_795", + "status": "in_progress", + "severity": "medium", + "summary": "Pond-crate scuff rate above threshold", + }, + { + "alert_id": "frog_qa_802", + "status": "resolved", + "severity": "low", + "summary": "Froge label alignment issue corrected", + }, + ], + } + + raise ValueError(f"Unknown tool: {name}") + + +def run_response( + *, + connection: ResponsesConnection, + model: str, + previous_response_id: Optional[str], + input_payload: Union[str, ResponseInputParam], + tools: list[FunctionToolParam], + tool_choice: ToolChoice, + show_events: bool, +) -> RunResponseResult: + connection.response.create( + model=model, + input=input_payload, + stream=True, + previous_response_id=previous_response_id, + tools=tools, + tool_choice=tool_choice, + ) + + text_parts: list[str] = [] + function_calls: list[FunctionCallRequest] = [] + response_id: Optional[str] = None + + for event in connection: + if event.type == "response.output_text.delta": + text_parts.append(event.delta) + continue + + if event.type == "response.output_item.done" and event.item.type == "function_call": + function_calls.append( + FunctionCallRequest( + name=event.item.name, + arguments_json=event.item.arguments, + call_id=event.item.call_id, + ) + ) + continue + + if getattr(event, "type", None) == "error": + raise RuntimeError(f"WebSocket error event: {event!r}") + + if isinstance(event, (ResponseCompletedEvent, ResponseFailedEvent, ResponseIncompleteEvent)): + response_id = event.response.id + if not isinstance(event, ResponseCompletedEvent): + raise RuntimeError(f"Response ended with {event.type} (id={response_id})") + if show_events: + print(f"[{event.type}]") + break + + if getattr(event, "type", None) == "response.done": + # Responses over WebSocket currently emit `response.done` as the final event. + # The payload still includes `response.id`, which we use for chaining. + event_response = getattr(event, "response", None) + event_response_id: Optional[str] = None + if isinstance(event_response, dict): + event_response_dict = cast(Dict[str, object], event_response) + raw_event_response_id = event_response_dict.get("id") + if isinstance(raw_event_response_id, str): + event_response_id = raw_event_response_id + else: + raw_event_response_id = getattr(event_response, "id", None) + if isinstance(raw_event_response_id, str): + event_response_id = raw_event_response_id + + if not isinstance(event_response_id, str): + raise RuntimeError(f"response.done event did not include a valid response.id: {event!r}") + + response_id = event_response_id + if show_events: + print("[response.done]") + break + + if show_events: + print(f"[{event.type}]") + + if response_id is None: + raise RuntimeError("No terminal response event received.") + + return RunResponseResult( + text="".join(text_parts), + response_id=response_id, + function_calls=function_calls, + ) + + +def run_turn( + *, + connection: ResponsesConnection, + model: str, + previous_response_id: Optional[str], + turn_prompt: str, + forced_tool_name: ToolName, + show_events: bool, + show_tool_io: bool, +) -> RunTurnResult: + accumulated_text_parts: list[str] = [] + + current_input: Union[str, ResponseInputParam] = turn_prompt + current_tool_choice: ToolChoice = {"type": "function", "name": forced_tool_name} + current_previous_response_id = previous_response_id + + while True: + response_result = run_response( + connection=connection, + model=model, + previous_response_id=current_previous_response_id, + input_payload=current_input, + tools=TOOLS, + tool_choice=current_tool_choice, + show_events=show_events, + ) + + if response_result.text: + accumulated_text_parts.append(response_result.text) + + current_previous_response_id = response_result.response_id + if not response_result.function_calls: + break + + tool_outputs: ResponseInputParam = [] + for function_call in response_result.function_calls: + tool_name = parse_tool_name(function_call.name) + arguments = parse_sku_arguments(function_call.arguments_json) + output_payload = call_tool(tool_name, arguments) + if show_tool_io: + print(f"[tool_call] {function_call.name}({function_call.arguments_json})") + print(f"[tool_output] {json.dumps(output_payload)}") + + function_call_output: FunctionCallOutputItem = { + "type": "function_call_output", + "call_id": function_call.call_id, + "output": json.dumps(output_payload), + } + tool_outputs.append(cast(ResponseInputItemParam, function_call_output)) + + current_input = tool_outputs + current_tool_choice = "none" + + return RunTurnResult( + assistant_text="".join(accumulated_text_parts).strip(), + response_id=current_previous_response_id, + ) + + +def main() -> None: + args = parse_args() + + client = OpenAI() + extra_headers = {"OpenAI-Beta": BETA_HEADER_VALUE} if args.use_beta_header else {} + + with client.responses.connect(extra_headers=extra_headers) as connection: + previous_response_id: Optional[str] = None + for index, turn in enumerate(DEMO_TURNS, start=1): + print(f"\n=== Turn {index} ===") + print(f"User: {turn['prompt']}") + turn_result = run_turn( + connection=connection, + model=args.model, + previous_response_id=previous_response_id, + turn_prompt=turn["prompt"], + forced_tool_name=turn["tool_name"], + show_events=args.show_events, + show_tool_io=args.show_tool_io, + ) + previous_response_id = turn_result.response_id + print(f"Assistant: {turn_result.assistant_text}") + + +if __name__ == "__main__": + main() diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md index 6eb1b999fa..36c95d7b83 100644 --- a/src/openai/resources/responses/api.md +++ b/src/openai/resources/responses/api.md @@ -127,6 +127,8 @@ from openai.types.responses import ( ResponseWebSearchCallCompletedEvent, ResponseWebSearchCallInProgressEvent, ResponseWebSearchCallSearchingEvent, + ResponsesClientEvent, + ResponsesServerEvent, SkillReference, Tool, ToolChoiceAllowed, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 3e1eb53d88..2ad1e6716c 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -2,17 +2,22 @@ from __future__ import annotations +import json +import logging from copy import copy -from typing import Any, List, Type, Union, Iterable, Optional, cast +from types import TracebackType +from typing import TYPE_CHECKING, Any, List, Type, Union, Iterable, Iterator, Optional, AsyncIterator, cast from functools import partial from typing_extensions import Literal, overload import httpx +from pydantic import BaseModel from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from ..._utils import is_given, maybe_transform, async_maybe_transform +from ..._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property +from ..._models import construct_type_unchecked from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .input_items import ( @@ -33,11 +38,13 @@ InputTokensWithStreamingResponse, AsyncInputTokensWithStreamingResponse, ) -from ..._base_client import make_request_options +from ..._exceptions import OpenAIError +from ..._base_client import _merge_mappings, make_request_options from ...types.responses import ( response_create_params, response_compact_params, response_retrieve_params, + responses_client_event_param, ) from ...lib._parsing._responses import ( TextFormatT, @@ -51,16 +58,28 @@ from ...types.responses.parsed_response import ParsedResponse from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager from ...types.responses.compacted_response import CompactedResponse +from ...types.websocket_connection_options import WebsocketConnectionOptions from ...types.responses.response_includable import ResponseIncludable from ...types.shared_params.responses_model import ResponsesModel from ...types.responses.response_input_param import ResponseInputParam from ...types.responses.response_prompt_param import ResponsePromptParam from ...types.responses.response_stream_event import ResponseStreamEvent +from ...types.responses.responses_client_event import ResponsesClientEvent +from ...types.responses.responses_server_event import ResponsesServerEvent from ...types.responses.response_input_item_param import ResponseInputItemParam from ...types.responses.response_text_config_param import ResponseTextConfigParam +from ...types.responses.responses_client_event_param import ResponsesClientEventParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebsocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebsocketConnection + + from ..._client import OpenAI, AsyncOpenAI __all__ = ["Responses", "AsyncResponses"] +log: logging.Logger = logging.getLogger(__name__) + class Responses(SyncAPIResource): @cached_property @@ -1701,6 +1720,23 @@ def compact( cast_to=CompactedResponse, ) + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> ResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events and receive response stream events over the socket. + """ + return ResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + ) + class AsyncResponses(AsyncAPIResource): @cached_property @@ -3345,6 +3381,23 @@ async def compact( cast_to=CompactedResponse, ) + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, + ) -> AsyncResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events and receive response stream events over the socket. + """ + return AsyncResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + ) + class ResponsesWithRawResponse: def __init__(self, responses: Responses) -> None: @@ -3504,3 +3557,491 @@ def _make_tools(tools: Iterable[ParseableToolParam] | Omit) -> List[ToolParam] | converted_tools.append(new_tool.cast()) return converted_tools + + +class AsyncResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: AsyncResponsesResponseResource + + _connection: AsyncWebsocketConnection + + def __init__(self, connection: AsyncWebsocketConnection) -> None: + self._connection = connection + + self.response = AsyncResponsesResponseResource(self) + + async def __aiter__(self) -> AsyncIterator[ResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield await self.recv() + except ConnectionClosedOK: + return + + async def recv(self) -> ResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `ResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(await self.recv_bytes()) + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug(f"Received websocket message: %s", message) + return message + + async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, ResponsesClientEventParam)) + ) + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + ResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, ResponsesServerEvent)), + ) + + +class AsyncResponsesConnectionManager: + """ + Context manager over a `AsyncResponsesConnection` that is returned by `responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__connection: AsyncResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + async def __aenter__(self) -> AsyncResponsesConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + try: + from websockets.asyncio.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **self.__extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + self.__connection = AsyncResponsesConnection( + await connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __aenter__ + + def _prepare_url(self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class ResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: ResponsesResponseResource + + _connection: WebsocketConnection + + def __init__(self, connection: WebsocketConnection) -> None: + self._connection = connection + + self.response = ResponsesResponseResource(self) + + def __iter__(self) -> Iterator[ResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK + + try: + while True: + yield self.recv() + except ConnectionClosedOK: + return + + def recv(self) -> ResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `ResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(self.recv_bytes()) + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `ResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug(f"Received websocket message: %s", message) + return message + + def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, ResponsesClientEventParam)) + ) + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> ResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `ResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + ResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, ResponsesServerEvent)), + ) + + +class ResponsesConnectionManager: + """ + Context manager over a `ResponsesConnection` that is returned by `responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebsocketConnectionOptions, + ) -> None: + self.__client = client + self.__connection: ResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + + def __enter__(self) -> ResponsesConnection: + """ + 👋 If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **self.__extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + self.__connection = ResponsesConnection( + connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + }, + self.__extra_headers, + ), + **self.__websocket_connection_options, + ) + ) + + return self.__connection + + enter = __enter__ + + def _prepare_url(self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + base_url = self.__client._base_url.copy_with(scheme="wss") + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseResponsesConnectionResource: + def __init__(self, connection: ResponsesConnection) -> None: + self._connection = connection + + +class ResponsesResponseResource(BaseResponsesConnectionResource): + def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[responses_client_event_param.ContextManagement]] | Omit = omit, + conversation: Optional[responses_client_event_param.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_options: Optional[responses_client_event_param.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: responses_client_event_param.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + self._connection.send( + cast( + ResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) + + +class BaseAsyncResponsesConnectionResource: + def __init__(self, connection: AsyncResponsesConnection) -> None: + self._connection = connection + + +class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource): + async def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[responses_client_event_param.ContextManagement]] | Omit = omit, + conversation: Optional[responses_client_event_param.Conversation] | Omit = omit, + include: Optional[List[ResponseIncludable]] | Omit = omit, + input: Union[str, ResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Metadata] | Omit = omit, + model: ResponsesModel | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[ResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + reasoning: Optional[Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_options: Optional[responses_client_event_param.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: ResponseTextConfigParam | Omit = omit, + tool_choice: responses_client_event_param.ToolChoice | Omit = omit, + tools: Iterable[ToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + await self._connection.send( + cast( + ResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index cbb8deae95..de6f68989b 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -13,6 +13,7 @@ from .response_item import ResponseItem as ResponseItem from .container_auto import ContainerAuto as ContainerAuto from .response_error import ResponseError as ResponseError +from .response_input import ResponseInput as ResponseInput from .response_usage import ResponseUsage as ResponseUsage from .parsed_response import ( ParsedContent as ParsedContent, @@ -72,6 +73,8 @@ from .response_create_params import ResponseCreateParams as ResponseCreateParams from .response_created_event import ResponseCreatedEvent as ResponseCreatedEvent from .response_input_content import ResponseInputContent as ResponseInputContent +from .responses_client_event import ResponsesClientEvent as ResponsesClientEvent +from .responses_server_event import ResponsesServerEvent as ResponsesServerEvent from .local_environment_param import LocalEnvironmentParam as LocalEnvironmentParam from .response_compact_params import ResponseCompactParams as ResponseCompactParams from .response_output_message import ResponseOutputMessage as ResponseOutputMessage @@ -121,6 +124,7 @@ from .response_input_content_param import ResponseInputContentParam as ResponseInputContentParam from .response_input_image_content import ResponseInputImageContent as ResponseInputImageContent from .response_refusal_delta_event import ResponseRefusalDeltaEvent as ResponseRefusalDeltaEvent +from .responses_client_event_param import ResponsesClientEventParam as ResponsesClientEventParam from .response_output_message_param import ResponseOutputMessageParam as ResponseOutputMessageParam from .response_output_refusal_param import ResponseOutputRefusalParam as ResponseOutputRefusalParam from .response_reasoning_item_param import ResponseReasoningItemParam as ResponseReasoningItemParam @@ -139,6 +143,7 @@ from .container_network_policy_disabled import ContainerNetworkPolicyDisabled as ContainerNetworkPolicyDisabled from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent +from .response_conversation_param_param import ResponseConversationParamParam as ResponseConversationParamParam from .response_format_text_config_param import ResponseFormatTextConfigParam as ResponseFormatTextConfigParam from .response_function_shell_tool_call import ResponseFunctionShellToolCall as ResponseFunctionShellToolCall from .response_function_tool_call_param import ResponseFunctionToolCallParam as ResponseFunctionToolCallParam diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py index 97ee4bf6ca..f8a2026537 100644 --- a/src/openai/types/responses/input_token_count_params.py +++ b/src/openai/types/responses/input_token_count_params.py @@ -15,8 +15,8 @@ from .response_input_item_param import ResponseInputItemParam from .tool_choice_allowed_param import ToolChoiceAllowedParam from .tool_choice_function_param import ToolChoiceFunctionParam -from .response_conversation_param import ResponseConversationParam from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam +from .response_conversation_param_param import ResponseConversationParamParam from .response_format_text_config_param import ResponseFormatTextConfigParam __all__ = ["InputTokenCountParams", "Conversation", "Text", "ToolChoice"] @@ -97,7 +97,7 @@ class InputTokenCountParams(TypedDict, total=False): """ -Conversation: TypeAlias = Union[str, ResponseConversationParam] +Conversation: TypeAlias = Union[str, ResponseConversationParamParam] class Text(TypedDict, total=False): diff --git a/src/openai/types/responses/response_conversation_param.py b/src/openai/types/responses/response_conversation_param.py index d1587fe68a..7db4129bf4 100644 --- a/src/openai/types/responses/response_conversation_param.py +++ b/src/openai/types/responses/response_conversation_param.py @@ -1,14 +1,12 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from __future__ import annotations - -from typing_extensions import Required, TypedDict +from ..._models import BaseModel __all__ = ["ResponseConversationParam"] -class ResponseConversationParam(TypedDict, total=False): +class ResponseConversationParam(BaseModel): """The conversation that this response belongs to.""" - id: Required[str] + id: str """The unique ID of the conversation.""" diff --git a/src/openai/types/responses/response_conversation_param_param.py b/src/openai/types/responses/response_conversation_param_param.py new file mode 100644 index 0000000000..dba3628d0b --- /dev/null +++ b/src/openai/types/responses/response_conversation_param_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ResponseConversationParamParam"] + + +class ResponseConversationParamParam(TypedDict, total=False): + """The conversation that this response belongs to.""" + + id: Required[str] + """The unique ID of the conversation.""" diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 0e78d1559b..bf7170da1f 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -19,9 +19,9 @@ from .tool_choice_allowed_param import ToolChoiceAllowedParam from .response_text_config_param import ResponseTextConfigParam from .tool_choice_function_param import ToolChoiceFunctionParam -from .response_conversation_param import ResponseConversationParam from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam from ..shared_params.responses_model import ResponsesModel +from .response_conversation_param_param import ResponseConversationParamParam __all__ = [ "ResponseCreateParamsBase", @@ -292,7 +292,7 @@ class ContextManagement(TypedDict, total=False): """Token threshold at which compaction should be triggered for this entry.""" -Conversation: TypeAlias = Union[str, ResponseConversationParam] +Conversation: TypeAlias = Union[str, ResponseConversationParamParam] class StreamOptions(TypedDict, total=False): diff --git a/src/openai/types/responses/response_input.py b/src/openai/types/responses/response_input.py new file mode 100644 index 0000000000..e2180dec05 --- /dev/null +++ b/src/openai/types/responses/response_input.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .response_input_item import ResponseInputItem + +__all__ = ["ResponseInput"] + +ResponseInput: TypeAlias = List[ResponseInputItem] diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py new file mode 100644 index 0000000000..2bc6f899c5 --- /dev/null +++ b/src/openai/types/responses/responses_client_event.py @@ -0,0 +1,326 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from .tool import Tool +from ..._models import BaseModel +from .response_input import ResponseInput +from .response_prompt import ResponsePrompt +from .tool_choice_mcp import ToolChoiceMcp +from ..shared.metadata import Metadata +from ..shared.reasoning import Reasoning +from .tool_choice_shell import ToolChoiceShell +from .tool_choice_types import ToolChoiceTypes +from .tool_choice_custom import ToolChoiceCustom +from .response_includable import ResponseIncludable +from .tool_choice_allowed import ToolChoiceAllowed +from .tool_choice_options import ToolChoiceOptions +from .response_text_config import ResponseTextConfig +from .tool_choice_function import ToolChoiceFunction +from ..shared.responses_model import ResponsesModel +from .tool_choice_apply_patch import ToolChoiceApplyPatch +from .response_conversation_param import ResponseConversationParam + +__all__ = ["ResponsesClientEvent", "ContextManagement", "Conversation", "StreamOptions", "ToolChoice"] + + +class ContextManagement(BaseModel): + type: str + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] = None + """Token threshold at which compaction should be triggered for this entry.""" + + +Conversation: TypeAlias = Union[str, ResponseConversationParam, None] + + +class StreamOptions(BaseModel): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: Optional[bool] = None + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowed, + ToolChoiceTypes, + ToolChoiceFunction, + ToolChoiceMcp, + ToolChoiceCustom, + ToolChoiceApplyPatch, + ToolChoiceShell, +] + + +class ResponsesClientEvent(BaseModel): + type: Literal["response.create"] + """The type of the client event. Always `response.create`.""" + + background: Optional[bool] = None + """ + Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + """ + + context_management: Optional[List[ContextManagement]] = None + """Context management configuration for this request.""" + + conversation: Optional[Conversation] = None + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[ResponseIncludable]] = None + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, ResponseInput, None] = None + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + """ + + instructions: Optional[str] = None + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] = None + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Metadata] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Optional[ResponsesModel] = None + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + parallel_tool_calls: Optional[bool] = None + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[ResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] = None + """The retention policy for the prompt cache. + + Set to `24h` to enable extended prompt caching, which keeps cached prefixes + active for longer, up to a maximum of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + """ + + reasoning: Optional[Reasoning] = None + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] = None + """Whether to store the generated model response for later retrieval via API.""" + + stream: Optional[bool] = None + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + stream_options: Optional[StreamOptions] = None + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: Optional[ResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ToolChoice] = None + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Optional[List[Tool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] = None + """ + An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: Optional[str] = None + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py new file mode 100644 index 0000000000..08596ef9ea --- /dev/null +++ b/src/openai/types/responses/responses_client_event_param.py @@ -0,0 +1,327 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .tool_param import ToolParam +from .response_includable import ResponseIncludable +from .tool_choice_options import ToolChoiceOptions +from .response_input_param import ResponseInputParam +from .response_prompt_param import ResponsePromptParam +from .tool_choice_mcp_param import ToolChoiceMcpParam +from ..shared_params.metadata import Metadata +from .tool_choice_shell_param import ToolChoiceShellParam +from .tool_choice_types_param import ToolChoiceTypesParam +from ..shared_params.reasoning import Reasoning +from .tool_choice_custom_param import ToolChoiceCustomParam +from .tool_choice_allowed_param import ToolChoiceAllowedParam +from .response_text_config_param import ResponseTextConfigParam +from .tool_choice_function_param import ToolChoiceFunctionParam +from .tool_choice_apply_patch_param import ToolChoiceApplyPatchParam +from ..shared_params.responses_model import ResponsesModel +from .response_conversation_param_param import ResponseConversationParamParam + +__all__ = ["ResponsesClientEventParam", "ContextManagement", "Conversation", "StreamOptions", "ToolChoice"] + + +class ContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + +Conversation: TypeAlias = Union[str, ResponseConversationParamParam] + + +class StreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +ToolChoice: TypeAlias = Union[ + ToolChoiceOptions, + ToolChoiceAllowedParam, + ToolChoiceTypesParam, + ToolChoiceFunctionParam, + ToolChoiceMcpParam, + ToolChoiceCustomParam, + ToolChoiceApplyPatchParam, + ToolChoiceShellParam, +] + + +class ResponsesClientEventParam(TypedDict, total=False): + type: Required[Literal["response.create"]] + """The type of the client event. Always `response.create`.""" + + background: Optional[bool] + """ + Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + """ + + context_management: Optional[Iterable[ContextManagement]] + """Context management configuration for this request.""" + + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[ResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, ResponseInputParam] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + """ + + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Metadata] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: ResponsesModel + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[ResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + prompt_cache_key: str + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + + prompt_cache_retention: Optional[Literal["in-memory", "24h"]] + """The retention policy for the prompt cache. + + Set to `24h` to enable extended prompt caching, which keeps cached prefixes + active for longer, up to a maximum of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + """ + + reasoning: Optional[Reasoning] + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + safety_identifier: str + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API.""" + + stream: Optional[bool] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + stream_options: Optional[StreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: ResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[ToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the number of most likely tokens to + return at each token position, each with an associated log probability. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ diff --git a/src/openai/types/responses/responses_server_event.py b/src/openai/types/responses/responses_server_event.py new file mode 100644 index 0000000000..c543587ad0 --- /dev/null +++ b/src/openai/types/responses/responses_server_event.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .response_error_event import ResponseErrorEvent +from .response_failed_event import ResponseFailedEvent +from .response_queued_event import ResponseQueuedEvent +from .response_created_event import ResponseCreatedEvent +from .response_completed_event import ResponseCompletedEvent +from .response_text_done_event import ResponseTextDoneEvent +from .response_audio_done_event import ResponseAudioDoneEvent +from .response_incomplete_event import ResponseIncompleteEvent +from .response_text_delta_event import ResponseTextDeltaEvent +from .response_audio_delta_event import ResponseAudioDeltaEvent +from .response_in_progress_event import ResponseInProgressEvent +from .response_refusal_done_event import ResponseRefusalDoneEvent +from .response_refusal_delta_event import ResponseRefusalDeltaEvent +from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent +from .response_output_item_done_event import ResponseOutputItemDoneEvent +from .response_content_part_done_event import ResponseContentPartDoneEvent +from .response_output_item_added_event import ResponseOutputItemAddedEvent +from .response_content_part_added_event import ResponseContentPartAddedEvent +from .response_mcp_call_completed_event import ResponseMcpCallCompletedEvent +from .response_reasoning_text_done_event import ResponseReasoningTextDoneEvent +from .response_mcp_call_in_progress_event import ResponseMcpCallInProgressEvent +from .response_reasoning_text_delta_event import ResponseReasoningTextDeltaEvent +from .response_audio_transcript_done_event import ResponseAudioTranscriptDoneEvent +from .response_mcp_list_tools_failed_event import ResponseMcpListToolsFailedEvent +from .response_audio_transcript_delta_event import ResponseAudioTranscriptDeltaEvent +from .response_mcp_call_arguments_done_event import ResponseMcpCallArgumentsDoneEvent +from .response_image_gen_call_completed_event import ResponseImageGenCallCompletedEvent +from .response_mcp_call_arguments_delta_event import ResponseMcpCallArgumentsDeltaEvent +from .response_mcp_list_tools_completed_event import ResponseMcpListToolsCompletedEvent +from .response_image_gen_call_generating_event import ResponseImageGenCallGeneratingEvent +from .response_web_search_call_completed_event import ResponseWebSearchCallCompletedEvent +from .response_web_search_call_searching_event import ResponseWebSearchCallSearchingEvent +from .response_file_search_call_completed_event import ResponseFileSearchCallCompletedEvent +from .response_file_search_call_searching_event import ResponseFileSearchCallSearchingEvent +from .response_image_gen_call_in_progress_event import ResponseImageGenCallInProgressEvent +from .response_mcp_list_tools_in_progress_event import ResponseMcpListToolsInProgressEvent +from .response_custom_tool_call_input_done_event import ResponseCustomToolCallInputDoneEvent +from .response_reasoning_summary_part_done_event import ResponseReasoningSummaryPartDoneEvent +from .response_reasoning_summary_text_done_event import ResponseReasoningSummaryTextDoneEvent +from .response_web_search_call_in_progress_event import ResponseWebSearchCallInProgressEvent +from .response_custom_tool_call_input_delta_event import ResponseCustomToolCallInputDeltaEvent +from .response_file_search_call_in_progress_event import ResponseFileSearchCallInProgressEvent +from .response_function_call_arguments_done_event import ResponseFunctionCallArgumentsDoneEvent +from .response_image_gen_call_partial_image_event import ResponseImageGenCallPartialImageEvent +from .response_output_text_annotation_added_event import ResponseOutputTextAnnotationAddedEvent +from .response_reasoning_summary_part_added_event import ResponseReasoningSummaryPartAddedEvent +from .response_reasoning_summary_text_delta_event import ResponseReasoningSummaryTextDeltaEvent +from .response_function_call_arguments_delta_event import ResponseFunctionCallArgumentsDeltaEvent +from .response_code_interpreter_call_code_done_event import ResponseCodeInterpreterCallCodeDoneEvent +from .response_code_interpreter_call_completed_event import ResponseCodeInterpreterCallCompletedEvent +from .response_code_interpreter_call_code_delta_event import ResponseCodeInterpreterCallCodeDeltaEvent +from .response_code_interpreter_call_in_progress_event import ResponseCodeInterpreterCallInProgressEvent +from .response_code_interpreter_call_interpreting_event import ResponseCodeInterpreterCallInterpretingEvent + +__all__ = ["ResponsesServerEvent"] + +ResponsesServerEvent: TypeAlias = Annotated[ + Union[ + ResponseAudioDeltaEvent, + ResponseAudioDoneEvent, + ResponseAudioTranscriptDeltaEvent, + ResponseAudioTranscriptDoneEvent, + ResponseCodeInterpreterCallCodeDeltaEvent, + ResponseCodeInterpreterCallCodeDoneEvent, + ResponseCodeInterpreterCallCompletedEvent, + ResponseCodeInterpreterCallInProgressEvent, + ResponseCodeInterpreterCallInterpretingEvent, + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseErrorEvent, + ResponseFileSearchCallCompletedEvent, + ResponseFileSearchCallInProgressEvent, + ResponseFileSearchCallSearchingEvent, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionCallArgumentsDoneEvent, + ResponseInProgressEvent, + ResponseFailedEvent, + ResponseIncompleteEvent, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseReasoningSummaryPartAddedEvent, + ResponseReasoningSummaryPartDoneEvent, + ResponseReasoningSummaryTextDeltaEvent, + ResponseReasoningSummaryTextDoneEvent, + ResponseReasoningTextDeltaEvent, + ResponseReasoningTextDoneEvent, + ResponseRefusalDeltaEvent, + ResponseRefusalDoneEvent, + ResponseTextDeltaEvent, + ResponseTextDoneEvent, + ResponseWebSearchCallCompletedEvent, + ResponseWebSearchCallInProgressEvent, + ResponseWebSearchCallSearchingEvent, + ResponseImageGenCallCompletedEvent, + ResponseImageGenCallGeneratingEvent, + ResponseImageGenCallInProgressEvent, + ResponseImageGenCallPartialImageEvent, + ResponseMcpCallArgumentsDeltaEvent, + ResponseMcpCallArgumentsDoneEvent, + ResponseMcpCallCompletedEvent, + ResponseMcpCallFailedEvent, + ResponseMcpCallInProgressEvent, + ResponseMcpListToolsCompletedEvent, + ResponseMcpListToolsFailedEvent, + ResponseMcpListToolsInProgressEvent, + ResponseOutputTextAnnotationAddedEvent, + ResponseQueuedEvent, + ResponseCustomToolCallInputDeltaEvent, + ResponseCustomToolCallInputDoneEvent, + ], + PropertyInfo(discriminator="type"), +] From 481ff6ef4c050469fa971746fb14b675d90a2e56 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:08:32 +0000 Subject: [PATCH 248/408] release: 2.22.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 21 +++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 49df20d2b8..c4563a87af 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.21.0" + ".": "2.22.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f558b3626..34bfd056ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 2.22.0 (2026-02-23) + +Full Changelog: [v2.21.0...v2.22.0](https://github.com/openai/openai-python/compare/v2.21.0...v2.22.0) + +### Features + +* **api:** websockets for responses api ([c01f6fb](https://github.com/openai/openai-python/commit/c01f6fb0d55b7454f73c4904ea7a1954553085dc)) + + +### Chores + +* **internal:** add request options to SSE classes ([cdb4315](https://github.com/openai/openai-python/commit/cdb4315ee29d5260bb373625d74cb523b4e3859c)) +* update mock server docs ([91f4da8](https://github.com/openai/openai-python/commit/91f4da80ec3dba5d3566961560dfd6feb9c2feb0)) + + +### Documentation + +* **api:** add batch size limit to file_batches parameter descriptions ([16ae76a](https://github.com/openai/openai-python/commit/16ae76a20a47f94c91ee2ca0b2ada274633abab3)) +* **api:** enhance method descriptions across audio, chat, realtime, skills, uploads, videos ([21f9e5a](https://github.com/openai/openai-python/commit/21f9e5aaf6ae27f0235fddb3ffa30fe73337f59b)) +* **api:** update safety_identifier documentation in chat completions and responses ([d74bfff](https://github.com/openai/openai-python/commit/d74bfff62c1c2b32d4dc88fd47ae7b1b2a962017)) + ## 2.21.0 (2026-02-13) Full Changelog: [v2.20.0...v2.21.0](https://github.com/openai/openai-python/compare/v2.20.0...v2.21.0) diff --git a/pyproject.toml b/pyproject.toml index fe2e394592..4358af79c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.21.0" +version = "2.22.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 0d4ef7b71c..7bea27c44f 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.21.0" # x-release-please-version +__version__ = "2.22.0" # x-release-please-version From 588d239c1073442f982165d310f9ccf09f631f7c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:32:05 +0000 Subject: [PATCH 249/408] chore(internal): make `test_proxy_environment_variables` more resilient --- tests/test_client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index 396f6dea99..7e8954d8a1 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1036,6 +1036,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has this set + monkeypatch.delenv("HTTP_PROXY", raising=False) client = DefaultHttpxClient() @@ -2079,6 +2081,8 @@ async def test_get_platform(self) -> None: async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has this set + monkeypatch.delenv("HTTP_PROXY", raising=False) client = DefaultAsyncHttpxClient() From 9e9a4f1712ec0ec637ca4f6e3f50e1647d268576 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 23:06:41 +0000 Subject: [PATCH 250/408] feat(api): add gpt-realtime-1.5 and gpt-audio-1.5 model options to realtime calls --- .stats.yml | 4 ++-- src/openai/resources/realtime/calls.py | 4 ++++ src/openai/types/realtime/call_accept_params.py | 2 ++ src/openai/types/realtime/realtime_session_create_request.py | 2 ++ .../types/realtime/realtime_session_create_request_param.py | 2 ++ src/openai/types/realtime/realtime_session_create_response.py | 2 ++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6d954e3ef8..365da3794f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-95886b357a553078e7f15505fe1c518c7daf11506946049c75eadf89d44da863.yml -openapi_spec_hash: 8dfdf1e1d1dbe58b236b89203aa2a1b0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a0aa54a302fbd7fff4ed7ad8a8547587d37b63324fc4af652bfa685ee9f8da44.yml +openapi_spec_hash: e45c5af19307cfc8b9baa4b8f8e865a0 config_hash: 4c2841519fd72fe44c18de4c18db231f diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index 20a22fc3b6..1520a97b0a 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -116,6 +116,7 @@ def accept( str, Literal[ "gpt-realtime", + "gpt-realtime-1.5", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -126,6 +127,7 @@ def accept( "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-audio-mini-2025-12-15", @@ -443,6 +445,7 @@ async def accept( str, Literal[ "gpt-realtime", + "gpt-realtime-1.5", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -453,6 +456,7 @@ async def accept( "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-audio-mini-2025-12-15", diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index d950f59f69..6d8caf9306 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -56,6 +56,7 @@ class CallAcceptParams(TypedDict, total=False): str, Literal[ "gpt-realtime", + "gpt-realtime-1.5", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -66,6 +67,7 @@ class CallAcceptParams(TypedDict, total=False): "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-audio-mini-2025-12-15", diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 4a93c91c7d..e34136a10a 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -57,6 +57,7 @@ class RealtimeSessionCreateRequest(BaseModel): str, Literal[ "gpt-realtime", + "gpt-realtime-1.5", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -67,6 +68,7 @@ class RealtimeSessionCreateRequest(BaseModel): "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-audio-mini-2025-12-15", diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index dee63d0967..f3180c9ed6 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -58,6 +58,7 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): str, Literal[ "gpt-realtime", + "gpt-realtime-1.5", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -68,6 +69,7 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-audio-mini-2025-12-15", diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 15a200ca17..a74615bd60 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -460,6 +460,7 @@ class RealtimeSessionCreateResponse(BaseModel): str, Literal[ "gpt-realtime", + "gpt-realtime-1.5", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -470,6 +471,7 @@ class RealtimeSessionCreateResponse(BaseModel): "gpt-realtime-mini", "gpt-realtime-mini-2025-10-06", "gpt-realtime-mini-2025-12-15", + "gpt-audio-1.5", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-audio-mini-2025-12-15", From 650ccd90dcad0ce23c06b95d8d07911f19d52513 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 00:35:26 +0000 Subject: [PATCH 251/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 365da3794f..2e3e64a4ec 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a0aa54a302fbd7fff4ed7ad8a8547587d37b63324fc4af652bfa685ee9f8da44.yml openapi_spec_hash: e45c5af19307cfc8b9baa4b8f8e865a0 -config_hash: 4c2841519fd72fe44c18de4c18db231f +config_hash: 2cbce279be85ff86a2fabbc85d62b011 From 921c330d4baebb04bbdb8070e9ced539cf49d97a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 00:36:00 +0000 Subject: [PATCH 252/408] release: 2.23.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c4563a87af..29dfa68625 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.22.0" + ".": "2.23.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 34bfd056ba..31245bfe99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 2.23.0 (2026-02-24) + +Full Changelog: [v2.22.0...v2.23.0](https://github.com/openai/openai-python/compare/v2.22.0...v2.23.0) + +### Features + +* **api:** add gpt-realtime-1.5 and gpt-audio-1.5 model options to realtime calls ([3300b61](https://github.com/openai/openai-python/commit/3300b61e1d5a34c9d28ec9cebbebd0de1fa93aa6)) + + +### Chores + +* **internal:** make `test_proxy_environment_variables` more resilient ([6b441e2](https://github.com/openai/openai-python/commit/6b441e2c43df60a773f62308e918d76b8eb3c4d3)) + ## 2.22.0 (2026-02-23) Full Changelog: [v2.21.0...v2.22.0](https://github.com/openai/openai-python/compare/v2.21.0...v2.22.0) diff --git a/pyproject.toml b/pyproject.toml index 4358af79c9..0803c313d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.22.0" +version = "2.23.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7bea27c44f..693ba82537 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.22.0" # x-release-please-version +__version__ = "2.23.0" # x-release-please-version From 656e3cab4a18262a49b961d41293367e45ee71b9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:01:04 -0800 Subject: [PATCH 253/408] release: 2.24.0 (#2890) * chore(internal): refactor sse event parsing * codegen metadata * chore(internal): make `test_proxy_environment_variables` more resilient to env * feat(api): add phase * fix(api): phase docs * fix(api): fix phase enum * release: 2.24.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +++--- CHANGELOG.md | 20 +++++++++++++++++++ pyproject.toml | 2 +- src/openai/_base_client.py | 4 ++++ src/openai/_models.py | 2 ++ src/openai/_streaming.py | 18 +++++++++++++---- src/openai/_types.py | 1 + src/openai/_version.py | 2 +- .../resources/beta/threads/runs/runs.py | 14 +++++++++++-- src/openai/resources/beta/threads/threads.py | 12 +++++++++-- src/openai/resources/responses/responses.py | 8 ++++++++ .../types/responses/easy_input_message.py | 9 +++++++++ .../responses/easy_input_message_param.py | 11 +++++++++- .../responses/response_compact_params.py | 3 +++ .../responses/response_output_message.py | 11 +++++++++- .../response_output_message_param.py | 11 +++++++++- .../api_resources/conversations/test_items.py | 2 ++ tests/api_resources/test_conversations.py | 2 ++ tests/api_resources/test_responses.py | 2 ++ tests/test_client.py | 16 +++++++++++++-- 21 files changed, 139 insertions(+), 19 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 29dfa68625..288087c17b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.23.0" + ".": "2.24.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 2e3e64a4ec..476a5b7658 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a0aa54a302fbd7fff4ed7ad8a8547587d37b63324fc4af652bfa685ee9f8da44.yml -openapi_spec_hash: e45c5af19307cfc8b9baa4b8f8e865a0 -config_hash: 2cbce279be85ff86a2fabbc85d62b011 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6bfe886b5ded0fe3bf37ca672698814e16e0836a093ceef65dac37ae44d1ad6b.yml +openapi_spec_hash: 6b1344a59044318e824c8d1af96033c7 +config_hash: 7f49c38fa3abe9b7038ffe62262c4912 diff --git a/CHANGELOG.md b/CHANGELOG.md index 31245bfe99..91da3fc859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 2.24.0 (2026-02-24) + +Full Changelog: [v2.23.0...v2.24.0](https://github.com/openai/openai-python/compare/v2.23.0...v2.24.0) + +### Features + +* **api:** add phase ([391deb9](https://github.com/openai/openai-python/commit/391deb99f6a92e51bffb25efd8dfe367d144bb9d)) + + +### Bug Fixes + +* **api:** fix phase enum ([42ebf7c](https://github.com/openai/openai-python/commit/42ebf7c30b7e27a175c0d75fcf42c8dc858e56d6)) +* **api:** phase docs ([7ddc61c](https://github.com/openai/openai-python/commit/7ddc61cd0f7825d5e7f3a10daf809135511d8d20)) + + +### Chores + +* **internal:** make `test_proxy_environment_variables` more resilient to env ([65af8fd](https://github.com/openai/openai-python/commit/65af8fd8550e99236e3f4dcb035312441788157a)) +* **internal:** refactor sse event parsing ([2344600](https://github.com/openai/openai-python/commit/23446008f06fb474d8c75d14a1bce26f4c5b95d8)) + ## 2.23.0 (2026-02-24) Full Changelog: [v2.22.0...v2.23.0](https://github.com/openai/openai-python/compare/v2.22.0...v2.23.0) diff --git a/pyproject.toml b/pyproject.toml index 0803c313d1..49ab3668e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.23.0" +version = "2.24.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 17863bc067..cf4571bf45 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1984,6 +1984,7 @@ def make_request_options( idempotency_key: str | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, post_parser: PostParser | NotGiven = not_given, + synthesize_event_and_data: bool | None = None, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} @@ -2009,6 +2010,9 @@ def make_request_options( # internal options["post_parser"] = post_parser # type: ignore + if synthesize_event_and_data is not None: + options["synthesize_event_and_data"] = synthesize_event_and_data + return options diff --git a/src/openai/_models.py b/src/openai/_models.py index 5cca20c6f9..810e49dfc5 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -845,6 +845,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): json_data: Body extra_json: AnyMapping follow_redirects: bool + synthesize_event_and_data: bool @final @@ -859,6 +860,7 @@ class FinalRequestOptions(pydantic.BaseModel): idempotency_key: Union[str, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() follow_redirects: Union[bool, None] = None + synthesize_event_and_data: Optional[bool] = None content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None # It should be noted that we cannot use `json` here as that would override diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 86b81c324f..45c13cc11d 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -98,8 +98,13 @@ def __stream__(self) -> Iterator[_T]: body=data["error"], ) - yield process_data(data=data, cast_to=cast_to, response=response) - + yield process_data( + data={"data": data, "event": sse.event} + if self._options is not None and self._options.synthesize_event_and_data + else data, + cast_to=cast_to, + response=response, + ) finally: # Ensure the response is closed even if the consumer doesn't read all data response.close() @@ -203,8 +208,13 @@ async def __stream__(self) -> AsyncIterator[_T]: body=data["error"], ) - yield process_data(data=data, cast_to=cast_to, response=response) - + yield process_data( + data={"data": data, "event": sse.event} + if self._options is not None and self._options.synthesize_event_and_data + else data, + cast_to=cast_to, + response=response, + ) finally: # Ensure the response is closed even if the consumer doesn't read all data await response.aclose() diff --git a/src/openai/_types.py b/src/openai/_types.py index 42f9df2373..c55c6f808d 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -122,6 +122,7 @@ class RequestOptions(TypedDict, total=False): extra_json: AnyMapping idempotency_key: str follow_redirects: bool + synthesize_event_and_data: bool # Sentinel class used until PEP 0661 is accepted diff --git a/src/openai/_version.py b/src/openai/_version.py index 693ba82537..08cf29390a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.23.0" # x-release-please-version +__version__ = "2.24.0" # x-release-please-version diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 8a58e91f69..90845a2f62 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -620,6 +620,7 @@ def create( extra_body=extra_body, timeout=timeout, query=maybe_transform({"include": include}, run_create_params.RunCreateParams), + synthesize_event_and_data=True, ), cast_to=Run, stream=stream or False, @@ -1368,7 +1369,11 @@ def submit_tool_outputs( else run_submit_tool_outputs_params.RunSubmitToolOutputsParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + synthesize_event_and_data=True, ), cast_to=Run, stream=stream or False, @@ -2075,6 +2080,7 @@ async def create( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"include": include}, run_create_params.RunCreateParams), + synthesize_event_and_data=True, ), cast_to=Run, stream=stream or False, @@ -2822,7 +2828,11 @@ async def submit_tool_outputs( else run_submit_tool_outputs_params.RunSubmitToolOutputsParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + synthesize_event_and_data=True, ), cast_to=Run, stream=stream or False, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 681d3c2933..a804fda159 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -729,7 +729,11 @@ def create_and_run( else thread_create_and_run_params.ThreadCreateAndRunParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + synthesize_event_and_data=True, ), cast_to=Run, stream=stream or False, @@ -1587,7 +1591,11 @@ async def create_and_run( else thread_create_and_run_params.ThreadCreateAndRunParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + synthesize_event_and_data=True, ), cast_to=Run, stream=stream or False, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 2ad1e6716c..c85a94495d 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1660,6 +1660,7 @@ def compact( input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1695,6 +1696,8 @@ def compact( [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`. + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1711,6 +1714,7 @@ def compact( "input": input, "instructions": instructions, "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, }, response_compact_params.ResponseCompactParams, ), @@ -3321,6 +3325,7 @@ async def compact( input: Union[str, Iterable[ResponseInputItemParam], None] | Omit = omit, instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3356,6 +3361,8 @@ async def compact( [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`. + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -3372,6 +3379,7 @@ async def compact( "input": input, "instructions": instructions, "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, }, response_compact_params.ResponseCompactParams, ), diff --git a/src/openai/types/responses/easy_input_message.py b/src/openai/types/responses/easy_input_message.py index 9a36a6b084..6f4d782734 100644 --- a/src/openai/types/responses/easy_input_message.py +++ b/src/openai/types/responses/easy_input_message.py @@ -30,5 +30,14 @@ class EasyInputMessage(BaseModel): One of `user`, `assistant`, `system`, or `developer`. """ + phase: Optional[Literal["commentary", "final_answer"]] = None + """The phase of an assistant message. + + Use `commentary` for an intermediate assistant message and `final_answer` for + the final assistant message. For follow-up requests with models like + `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. + Omitting it can degrade performance. Not used for user messages. + """ + type: Optional[Literal["message"]] = None """The type of the message input. Always `message`.""" diff --git a/src/openai/types/responses/easy_input_message_param.py b/src/openai/types/responses/easy_input_message_param.py index 0a382bddee..f7eb42ba71 100644 --- a/src/openai/types/responses/easy_input_message_param.py +++ b/src/openai/types/responses/easy_input_message_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union +from typing import Union, Optional from typing_extensions import Literal, Required, TypedDict from .response_input_message_content_list_param import ResponseInputMessageContentListParam @@ -31,5 +31,14 @@ class EasyInputMessageParam(TypedDict, total=False): One of `user`, `assistant`, `system`, or `developer`. """ + phase: Optional[Literal["commentary", "final_answer"]] + """The phase of an assistant message. + + Use `commentary` for an intermediate assistant message and `final_answer` for + the final assistant message. For follow-up requests with models like + `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. + Omitting it can degrade performance. Not used for user messages. + """ + type: Literal["message"] """The type of the message input. Always `message`.""" diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index 657c6a0764..4fb0e7ddc3 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -131,3 +131,6 @@ class ResponseCompactParams(TypedDict, total=False): [conversation state](https://platform.openai.com/docs/guides/conversation-state). Cannot be used in conjunction with `conversation`. """ + + prompt_cache_key: Optional[str] + """A key to use when reading from or writing to the prompt cache.""" diff --git a/src/openai/types/responses/response_output_message.py b/src/openai/types/responses/response_output_message.py index 9c1d1f97fc..a8720e1c57 100644 --- a/src/openai/types/responses/response_output_message.py +++ b/src/openai/types/responses/response_output_message.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union +from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -34,3 +34,12 @@ class ResponseOutputMessage(BaseModel): type: Literal["message"] """The type of the output message. Always `message`.""" + + phase: Optional[Literal["commentary", "final_answer"]] = None + """The phase of an assistant message. + + Use `commentary` for an intermediate assistant message and `final_answer` for + the final assistant message. For follow-up requests with models like + `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. + Omitting it can degrade performance. Not used for user messages. + """ diff --git a/src/openai/types/responses/response_output_message_param.py b/src/openai/types/responses/response_output_message_param.py index 9c2f5246a1..5d488d8c6b 100644 --- a/src/openai/types/responses/response_output_message_param.py +++ b/src/openai/types/responses/response_output_message_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .response_output_text_param import ResponseOutputTextParam @@ -34,3 +34,12 @@ class ResponseOutputMessageParam(TypedDict, total=False): type: Required[Literal["message"]] """The type of the output message. Always `message`.""" + + phase: Optional[Literal["commentary", "final_answer"]] + """The phase of an assistant message. + + Use `commentary` for an intermediate assistant message and `final_answer` for + the final assistant message. For follow-up requests with models like + `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. + Omitting it can degrade performance. Not used for user messages. + """ diff --git a/tests/api_resources/conversations/test_items.py b/tests/api_resources/conversations/test_items.py index 0503301f16..aca9568410 100644 --- a/tests/api_resources/conversations/test_items.py +++ b/tests/api_resources/conversations/test_items.py @@ -44,6 +44,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: { "content": "string", "role": "user", + "phase": "commentary", "type": "message", } ], @@ -285,6 +286,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> { "content": "string", "role": "user", + "phase": "commentary", "type": "message", } ], diff --git a/tests/api_resources/test_conversations.py b/tests/api_resources/test_conversations.py index d21e685a04..a451d339e8 100644 --- a/tests/api_resources/test_conversations.py +++ b/tests/api_resources/test_conversations.py @@ -32,6 +32,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: { "content": "string", "role": "user", + "phase": "commentary", "type": "message", } ], @@ -195,6 +196,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> { "content": "string", "role": "user", + "phase": "commentary", "type": "message", } ], diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index a644b2c6b9..8f82f2046d 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -385,6 +385,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None: input="string", instructions="instructions", previous_response_id="resp_123", + prompt_cache_key="prompt_cache_key", ) assert_matches_type(CompactedResponse, response, path=["response"]) @@ -793,6 +794,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) - input="string", instructions="instructions", previous_response_id="resp_123", + prompt_cache_key="prompt_cache_key", ) assert_matches_type(CompactedResponse, response, path=["response"]) diff --git a/tests/test_client.py b/tests/test_client.py index 7e8954d8a1..b8b963aa4c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1036,8 +1036,14 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") - # Delete in case our environment has this set + # Delete in case our environment has any proxy env vars set monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultHttpxClient() @@ -2081,8 +2087,14 @@ async def test_get_platform(self) -> None: async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") - # Delete in case our environment has this set + # Delete in case our environment has any proxy env vars set monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultAsyncHttpxClient() From 9b1bb6ee10d4a0a04d04b1f67907f3747c57f5a2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:34:23 -0500 Subject: [PATCH 254/408] release: 2.25.0 (#2891) * feat(api): remove prompt_cache_key param from responses, phase field from message types * fix(api): readd phase * fix(api): manual updates * fix(api): internal schema fixes * codegen metadata * chore(internal): codegen related update * chore(internal): reduce warnings * chore(internal): codegen related update * feat(api): gpt-5.4, tool search tool, and new computer tool * release: 2.25.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 6 +- CHANGELOG.md | 23 +++ api.md | 2 + pyproject.toml | 2 +- scripts/mock | 13 +- src/openai/_client.py | 108 +++++++++++ src/openai/_version.py | 2 +- src/openai/lib/_parsing/_responses.py | 2 + src/openai/resources/audio/audio.py | 18 ++ src/openai/resources/audio/speech.py | 4 + src/openai/resources/audio/transcriptions.py | 4 + src/openai/resources/audio/translations.py | 4 + src/openai/resources/batches.py | 4 + src/openai/resources/beta/assistants.py | 4 + src/openai/resources/beta/beta.py | 12 ++ src/openai/resources/beta/threads/messages.py | 4 + .../resources/beta/threads/runs/runs.py | 10 + .../resources/beta/threads/runs/steps.py | 4 + src/openai/resources/beta/threads/threads.py | 16 ++ src/openai/resources/chat/chat.py | 18 ++ .../resources/chat/completions/completions.py | 26 +++ .../resources/chat/completions/messages.py | 8 + src/openai/resources/completions.py | 8 + .../resources/conversations/conversations.py | 10 + src/openai/resources/conversations/items.py | 4 + src/openai/resources/embeddings.py | 8 + src/openai/resources/evals/evals.py | 10 + .../resources/evals/runs/output_items.py | 4 + src/openai/resources/evals/runs/runs.py | 10 + src/openai/resources/files.py | 8 + .../resources/fine_tuning/alpha/alpha.py | 6 + .../resources/fine_tuning/alpha/graders.py | 4 + .../fine_tuning/checkpoints/checkpoints.py | 6 + .../fine_tuning/checkpoints/permissions.py | 178 ++++++++++++++++- .../resources/fine_tuning/fine_tuning.py | 6 + .../resources/fine_tuning/jobs/checkpoints.py | 4 + src/openai/resources/fine_tuning/jobs/jobs.py | 10 + src/openai/resources/images.py | 4 + src/openai/resources/models.py | 4 + src/openai/resources/moderations.py | 8 + src/openai/resources/responses/api.md | 8 + src/openai/resources/responses/responses.py | 4 + src/openai/resources/uploads/parts.py | 4 + src/openai/resources/uploads/uploads.py | 10 + src/openai/resources/videos.py | 4 +- src/openai/resources/webhooks/__init__.py | 1 + .../types/beta/assistant_create_params.py | 5 +- .../beta/thread_create_and_run_params.py | 5 +- src/openai/types/beta/thread_create_params.py | 5 +- .../computer_screenshot_content.py | 6 + .../types/conversations/conversation_item.py | 4 + .../types/fine_tuning/checkpoints/__init__.py | 2 + .../checkpoints/permission_list_params.py | 21 ++ .../checkpoints/permission_list_response.py | 25 +++ .../realtime_response_create_mcp_tool.py | 3 + ...realtime_response_create_mcp_tool_param.py | 3 + .../realtime_session_create_response.py | 3 + .../realtime/realtime_tools_config_param.py | 3 + .../realtime/realtime_tools_config_union.py | 3 + .../realtime_tools_config_union_param.py | 3 + src/openai/types/responses/__init__.py | 18 ++ src/openai/types/responses/computer_action.py | 181 +++++++++++++++++ .../types/responses/computer_action_list.py | 10 + .../responses/computer_action_list_param.py | 183 ++++++++++++++++++ .../types/responses/computer_action_param.py | 180 +++++++++++++++++ .../types/responses/computer_use_tool.py | 17 ++ .../responses/computer_use_tool_param.py | 17 ++ src/openai/types/responses/custom_tool.py | 3 + .../types/responses/custom_tool_param.py | 3 + .../types/responses/easy_input_message.py | 11 +- .../responses/easy_input_message_param.py | 11 +- src/openai/types/responses/function_tool.py | 3 + .../types/responses/function_tool_param.py | 3 + src/openai/types/responses/namespace_tool.py | 41 ++++ .../types/responses/namespace_tool_param.py | 41 ++++ src/openai/types/responses/parsed_response.py | 4 + .../responses/response_compact_params.py | 2 + .../responses/response_computer_tool_call.py | 41 ++-- .../response_computer_tool_call_param.py | 41 ++-- .../responses/response_custom_tool_call.py | 3 + .../response_custom_tool_call_param.py | 3 + .../responses/response_function_tool_call.py | 3 + .../response_function_tool_call_param.py | 3 + .../types/responses/response_input_file.py | 6 + .../responses/response_input_file_content.py | 6 + .../response_input_file_content_param.py | 6 + .../responses/response_input_file_param.py | 6 + .../types/responses/response_input_image.py | 4 +- .../responses/response_input_image_content.py | 4 +- .../response_input_image_content_param.py | 4 +- .../responses/response_input_image_param.py | 4 +- .../types/responses/response_input_item.py | 24 +++ .../responses/response_input_item_param.py | 24 +++ .../types/responses/response_input_param.py | 24 +++ src/openai/types/responses/response_item.py | 4 + .../types/responses/response_output_item.py | 4 + .../responses/response_output_message.py | 11 +- .../response_output_message_param.py | 11 +- .../responses/response_tool_search_call.py | 31 +++ .../response_tool_search_output_item.py | 32 +++ .../response_tool_search_output_item_param.py | 29 +++ ...nse_tool_search_output_item_param_param.py | 30 +++ src/openai/types/responses/tool.py | 9 + .../types/responses/tool_choice_types.py | 4 + .../responses/tool_choice_types_param.py | 4 + src/openai/types/responses/tool_param.py | 9 + .../types/responses/tool_search_tool.py | 24 +++ .../types/responses/tool_search_tool_param.py | 24 +++ .../responses/web_search_preview_tool.py | 4 +- .../web_search_preview_tool_param.py | 4 +- src/openai/types/shared/chat_model.py | 2 + src/openai/types/shared_params/chat_model.py | 2 + src/openai/types/video.py | 9 +- src/openai/types/video_create_params.py | 2 +- tests/api_resources/chat/test_completions.py | 32 +-- .../checkpoints/test_permissions.py | 173 ++++++++++++++--- .../responses/test_input_tokens.py | 2 + tests/api_resources/test_responses.py | 20 +- tests/test_client.py | 24 +-- 120 files changed, 1984 insertions(+), 160 deletions(-) create mode 100644 src/openai/types/fine_tuning/checkpoints/permission_list_params.py create mode 100644 src/openai/types/fine_tuning/checkpoints/permission_list_response.py create mode 100644 src/openai/types/responses/computer_action.py create mode 100644 src/openai/types/responses/computer_action_list.py create mode 100644 src/openai/types/responses/computer_action_list_param.py create mode 100644 src/openai/types/responses/computer_action_param.py create mode 100644 src/openai/types/responses/computer_use_tool.py create mode 100644 src/openai/types/responses/computer_use_tool_param.py create mode 100644 src/openai/types/responses/namespace_tool.py create mode 100644 src/openai/types/responses/namespace_tool_param.py create mode 100644 src/openai/types/responses/response_tool_search_call.py create mode 100644 src/openai/types/responses/response_tool_search_output_item.py create mode 100644 src/openai/types/responses/response_tool_search_output_item_param.py create mode 100644 src/openai/types/responses/response_tool_search_output_item_param_param.py create mode 100644 src/openai/types/responses/tool_search_tool.py create mode 100644 src/openai/types/responses/tool_search_tool_param.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 288087c17b..c5fe8ab6d6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.24.0" + ".": "2.25.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 476a5b7658..a59953aaa9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-6bfe886b5ded0fe3bf37ca672698814e16e0836a093ceef65dac37ae44d1ad6b.yml -openapi_spec_hash: 6b1344a59044318e824c8d1af96033c7 -config_hash: 7f49c38fa3abe9b7038ffe62262c4912 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9c802d45a9bf2a896b5fd22ac22bba185e8a145bd40ed242df9bb87a05e954eb.yml +openapi_spec_hash: 97984ed69285e660b7d5c810c69ed449 +config_hash: acb0b1eb5d7284bfedaddb29f7f5a691 diff --git a/CHANGELOG.md b/CHANGELOG.md index 91da3fc859..780a961a43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 2.25.0 (2026-03-05) + +Full Changelog: [v2.24.0...v2.25.0](https://github.com/openai/openai-python/compare/v2.24.0...v2.25.0) + +### Features + +* **api:** gpt-5.4, tool search tool, and new computer tool ([6b2043f](https://github.com/openai/openai-python/commit/6b2043f3d63058f5582eab7a7705b30a3d5536f0)) +* **api:** remove prompt_cache_key param from responses, phase field from message types ([44fb382](https://github.com/openai/openai-python/commit/44fb382698872d98d5f72c880b47846c7b594f4f)) + + +### Bug Fixes + +* **api:** internal schema fixes ([0c0f970](https://github.com/openai/openai-python/commit/0c0f970cbd164131bf06f7ab38f170bbcb323683)) +* **api:** manual updates ([9fc323f](https://github.com/openai/openai-python/commit/9fc323f4da6cfca9de194e12c1486a3cd1bfa4b5)) +* **api:** readd phase ([1b27b5a](https://github.com/openai/openai-python/commit/1b27b5a834f5cb75f80c597259d0df0352ba83bd)) + + +### Chores + +* **internal:** codegen related update ([bdb837d](https://github.com/openai/openai-python/commit/bdb837d2c1d2a161cc4b22ef26e9e8446d5dc2a3)) +* **internal:** codegen related update ([b1de941](https://github.com/openai/openai-python/commit/b1de9419a68fd6fb97a63f415fb3d1e5851582cb)) +* **internal:** reduce warnings ([7cdbd06](https://github.com/openai/openai-python/commit/7cdbd06d3ca41af64d616b4b4bb61226cc38b662)) + ## 2.24.0 (2026-02-24) Full Changelog: [v2.23.0...v2.24.0](https://github.com/openai/openai-python/compare/v2.23.0...v2.24.0) diff --git a/api.md b/api.md index da521d3418..a7981f7185 100644 --- a/api.md +++ b/api.md @@ -309,6 +309,7 @@ Types: from openai.types.fine_tuning.checkpoints import ( PermissionCreateResponse, PermissionRetrieveResponse, + PermissionListResponse, PermissionDeleteResponse, ) ``` @@ -317,6 +318,7 @@ Methods: - client.fine_tuning.checkpoints.permissions.create(fine_tuned_model_checkpoint, \*\*params) -> SyncPage[PermissionCreateResponse] - client.fine_tuning.checkpoints.permissions.retrieve(fine_tuned_model_checkpoint, \*\*params) -> PermissionRetrieveResponse +- client.fine_tuning.checkpoints.permissions.list(fine_tuned_model_checkpoint, \*\*params) -> SyncConversationCursorPage[PermissionListResponse] - client.fine_tuning.checkpoints.permissions.delete(permission_id, \*, fine_tuned_model_checkpoint) -> PermissionDeleteResponse ## Alpha diff --git a/pyproject.toml b/pyproject.toml index 49ab3668e8..f7aae6cbdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.24.0" +version = "2.25.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/scripts/mock b/scripts/mock index 0b28f6ea23..bcf3b392b3 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,11 +21,22 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then + # Pre-install the package so the download doesn't eat into the startup timeout + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & - # Wait for server to come online + # Wait for server to come online (max 30s) echo -n "Waiting for server" + attempts=0 while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + attempts=$((attempts + 1)) + if [ "$attempts" -ge 300 ]; then + echo + echo "Timed out waiting for Prism server to start" + cat .prism.log + exit 1 + fi echo -n "." sleep 0.1 done diff --git a/src/openai/_client.py b/src/openai/_client.py index 0399bbf742..aadf3601f2 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -180,6 +180,9 @@ def __init__( @cached_property def completions(self) -> Completions: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ from .resources.completions import Completions return Completions(self) @@ -192,18 +195,25 @@ def chat(self) -> Chat: @cached_property def embeddings(self) -> Embeddings: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ from .resources.embeddings import Embeddings return Embeddings(self) @cached_property def files(self) -> Files: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ from .resources.files import Files return Files(self) @cached_property def images(self) -> Images: + """Given a prompt and/or an input image, the model will generate a new image.""" from .resources.images import Images return Images(self) @@ -216,12 +226,16 @@ def audio(self) -> Audio: @cached_property def moderations(self) -> Moderations: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ from .resources.moderations import Moderations return Moderations(self) @cached_property def models(self) -> Models: + """List and describe the various models available in the API.""" from .resources.models import Models return Models(self) @@ -252,12 +266,14 @@ def beta(self) -> Beta: @cached_property def batches(self) -> Batches: + """Create large batches of API requests to run asynchronously.""" from .resources.batches import Batches return Batches(self) @cached_property def uploads(self) -> Uploads: + """Use Uploads to upload large files in multiple parts.""" from .resources.uploads import Uploads return Uploads(self) @@ -276,12 +292,14 @@ def realtime(self) -> Realtime: @cached_property def conversations(self) -> Conversations: + """Manage conversations and conversation items.""" from .resources.conversations import Conversations return Conversations(self) @cached_property def evals(self) -> Evals: + """Manage and run evals in the OpenAI platform.""" from .resources.evals import Evals return Evals(self) @@ -537,6 +555,9 @@ def __init__( @cached_property def completions(self) -> AsyncCompletions: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ from .resources.completions import AsyncCompletions return AsyncCompletions(self) @@ -549,18 +570,25 @@ def chat(self) -> AsyncChat: @cached_property def embeddings(self) -> AsyncEmbeddings: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ from .resources.embeddings import AsyncEmbeddings return AsyncEmbeddings(self) @cached_property def files(self) -> AsyncFiles: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ from .resources.files import AsyncFiles return AsyncFiles(self) @cached_property def images(self) -> AsyncImages: + """Given a prompt and/or an input image, the model will generate a new image.""" from .resources.images import AsyncImages return AsyncImages(self) @@ -573,12 +601,16 @@ def audio(self) -> AsyncAudio: @cached_property def moderations(self) -> AsyncModerations: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ from .resources.moderations import AsyncModerations return AsyncModerations(self) @cached_property def models(self) -> AsyncModels: + """List and describe the various models available in the API.""" from .resources.models import AsyncModels return AsyncModels(self) @@ -609,12 +641,14 @@ def beta(self) -> AsyncBeta: @cached_property def batches(self) -> AsyncBatches: + """Create large batches of API requests to run asynchronously.""" from .resources.batches import AsyncBatches return AsyncBatches(self) @cached_property def uploads(self) -> AsyncUploads: + """Use Uploads to upload large files in multiple parts.""" from .resources.uploads import AsyncUploads return AsyncUploads(self) @@ -633,12 +667,14 @@ def realtime(self) -> AsyncRealtime: @cached_property def conversations(self) -> AsyncConversations: + """Manage conversations and conversation items.""" from .resources.conversations import AsyncConversations return AsyncConversations(self) @cached_property def evals(self) -> AsyncEvals: + """Manage and run evals in the OpenAI platform.""" from .resources.evals import AsyncEvals return AsyncEvals(self) @@ -805,6 +841,9 @@ def __init__(self, client: OpenAI) -> None: @cached_property def completions(self) -> completions.CompletionsWithRawResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ from .resources.completions import CompletionsWithRawResponse return CompletionsWithRawResponse(self._client.completions) @@ -817,18 +856,25 @@ def chat(self) -> chat.ChatWithRawResponse: @cached_property def embeddings(self) -> embeddings.EmbeddingsWithRawResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ from .resources.embeddings import EmbeddingsWithRawResponse return EmbeddingsWithRawResponse(self._client.embeddings) @cached_property def files(self) -> files.FilesWithRawResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ from .resources.files import FilesWithRawResponse return FilesWithRawResponse(self._client.files) @cached_property def images(self) -> images.ImagesWithRawResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" from .resources.images import ImagesWithRawResponse return ImagesWithRawResponse(self._client.images) @@ -841,12 +887,16 @@ def audio(self) -> audio.AudioWithRawResponse: @cached_property def moderations(self) -> moderations.ModerationsWithRawResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ from .resources.moderations import ModerationsWithRawResponse return ModerationsWithRawResponse(self._client.moderations) @cached_property def models(self) -> models.ModelsWithRawResponse: + """List and describe the various models available in the API.""" from .resources.models import ModelsWithRawResponse return ModelsWithRawResponse(self._client.models) @@ -871,12 +921,14 @@ def beta(self) -> beta.BetaWithRawResponse: @cached_property def batches(self) -> batches.BatchesWithRawResponse: + """Create large batches of API requests to run asynchronously.""" from .resources.batches import BatchesWithRawResponse return BatchesWithRawResponse(self._client.batches) @cached_property def uploads(self) -> uploads.UploadsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" from .resources.uploads import UploadsWithRawResponse return UploadsWithRawResponse(self._client.uploads) @@ -895,12 +947,14 @@ def realtime(self) -> realtime.RealtimeWithRawResponse: @cached_property def conversations(self) -> conversations.ConversationsWithRawResponse: + """Manage conversations and conversation items.""" from .resources.conversations import ConversationsWithRawResponse return ConversationsWithRawResponse(self._client.conversations) @cached_property def evals(self) -> evals.EvalsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" from .resources.evals import EvalsWithRawResponse return EvalsWithRawResponse(self._client.evals) @@ -932,6 +986,9 @@ def __init__(self, client: AsyncOpenAI) -> None: @cached_property def completions(self) -> completions.AsyncCompletionsWithRawResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ from .resources.completions import AsyncCompletionsWithRawResponse return AsyncCompletionsWithRawResponse(self._client.completions) @@ -944,18 +1001,25 @@ def chat(self) -> chat.AsyncChatWithRawResponse: @cached_property def embeddings(self) -> embeddings.AsyncEmbeddingsWithRawResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ from .resources.embeddings import AsyncEmbeddingsWithRawResponse return AsyncEmbeddingsWithRawResponse(self._client.embeddings) @cached_property def files(self) -> files.AsyncFilesWithRawResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ from .resources.files import AsyncFilesWithRawResponse return AsyncFilesWithRawResponse(self._client.files) @cached_property def images(self) -> images.AsyncImagesWithRawResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" from .resources.images import AsyncImagesWithRawResponse return AsyncImagesWithRawResponse(self._client.images) @@ -968,12 +1032,16 @@ def audio(self) -> audio.AsyncAudioWithRawResponse: @cached_property def moderations(self) -> moderations.AsyncModerationsWithRawResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ from .resources.moderations import AsyncModerationsWithRawResponse return AsyncModerationsWithRawResponse(self._client.moderations) @cached_property def models(self) -> models.AsyncModelsWithRawResponse: + """List and describe the various models available in the API.""" from .resources.models import AsyncModelsWithRawResponse return AsyncModelsWithRawResponse(self._client.models) @@ -998,12 +1066,14 @@ def beta(self) -> beta.AsyncBetaWithRawResponse: @cached_property def batches(self) -> batches.AsyncBatchesWithRawResponse: + """Create large batches of API requests to run asynchronously.""" from .resources.batches import AsyncBatchesWithRawResponse return AsyncBatchesWithRawResponse(self._client.batches) @cached_property def uploads(self) -> uploads.AsyncUploadsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" from .resources.uploads import AsyncUploadsWithRawResponse return AsyncUploadsWithRawResponse(self._client.uploads) @@ -1022,12 +1092,14 @@ def realtime(self) -> realtime.AsyncRealtimeWithRawResponse: @cached_property def conversations(self) -> conversations.AsyncConversationsWithRawResponse: + """Manage conversations and conversation items.""" from .resources.conversations import AsyncConversationsWithRawResponse return AsyncConversationsWithRawResponse(self._client.conversations) @cached_property def evals(self) -> evals.AsyncEvalsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" from .resources.evals import AsyncEvalsWithRawResponse return AsyncEvalsWithRawResponse(self._client.evals) @@ -1059,6 +1131,9 @@ def __init__(self, client: OpenAI) -> None: @cached_property def completions(self) -> completions.CompletionsWithStreamingResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ from .resources.completions import CompletionsWithStreamingResponse return CompletionsWithStreamingResponse(self._client.completions) @@ -1071,18 +1146,25 @@ def chat(self) -> chat.ChatWithStreamingResponse: @cached_property def embeddings(self) -> embeddings.EmbeddingsWithStreamingResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ from .resources.embeddings import EmbeddingsWithStreamingResponse return EmbeddingsWithStreamingResponse(self._client.embeddings) @cached_property def files(self) -> files.FilesWithStreamingResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ from .resources.files import FilesWithStreamingResponse return FilesWithStreamingResponse(self._client.files) @cached_property def images(self) -> images.ImagesWithStreamingResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" from .resources.images import ImagesWithStreamingResponse return ImagesWithStreamingResponse(self._client.images) @@ -1095,12 +1177,16 @@ def audio(self) -> audio.AudioWithStreamingResponse: @cached_property def moderations(self) -> moderations.ModerationsWithStreamingResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ from .resources.moderations import ModerationsWithStreamingResponse return ModerationsWithStreamingResponse(self._client.moderations) @cached_property def models(self) -> models.ModelsWithStreamingResponse: + """List and describe the various models available in the API.""" from .resources.models import ModelsWithStreamingResponse return ModelsWithStreamingResponse(self._client.models) @@ -1125,12 +1211,14 @@ def beta(self) -> beta.BetaWithStreamingResponse: @cached_property def batches(self) -> batches.BatchesWithStreamingResponse: + """Create large batches of API requests to run asynchronously.""" from .resources.batches import BatchesWithStreamingResponse return BatchesWithStreamingResponse(self._client.batches) @cached_property def uploads(self) -> uploads.UploadsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" from .resources.uploads import UploadsWithStreamingResponse return UploadsWithStreamingResponse(self._client.uploads) @@ -1149,12 +1237,14 @@ def realtime(self) -> realtime.RealtimeWithStreamingResponse: @cached_property def conversations(self) -> conversations.ConversationsWithStreamingResponse: + """Manage conversations and conversation items.""" from .resources.conversations import ConversationsWithStreamingResponse return ConversationsWithStreamingResponse(self._client.conversations) @cached_property def evals(self) -> evals.EvalsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" from .resources.evals import EvalsWithStreamingResponse return EvalsWithStreamingResponse(self._client.evals) @@ -1186,6 +1276,9 @@ def __init__(self, client: AsyncOpenAI) -> None: @cached_property def completions(self) -> completions.AsyncCompletionsWithStreamingResponse: + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ from .resources.completions import AsyncCompletionsWithStreamingResponse return AsyncCompletionsWithStreamingResponse(self._client.completions) @@ -1198,18 +1291,25 @@ def chat(self) -> chat.AsyncChatWithStreamingResponse: @cached_property def embeddings(self) -> embeddings.AsyncEmbeddingsWithStreamingResponse: + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ from .resources.embeddings import AsyncEmbeddingsWithStreamingResponse return AsyncEmbeddingsWithStreamingResponse(self._client.embeddings) @cached_property def files(self) -> files.AsyncFilesWithStreamingResponse: + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ from .resources.files import AsyncFilesWithStreamingResponse return AsyncFilesWithStreamingResponse(self._client.files) @cached_property def images(self) -> images.AsyncImagesWithStreamingResponse: + """Given a prompt and/or an input image, the model will generate a new image.""" from .resources.images import AsyncImagesWithStreamingResponse return AsyncImagesWithStreamingResponse(self._client.images) @@ -1222,12 +1322,16 @@ def audio(self) -> audio.AsyncAudioWithStreamingResponse: @cached_property def moderations(self) -> moderations.AsyncModerationsWithStreamingResponse: + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ from .resources.moderations import AsyncModerationsWithStreamingResponse return AsyncModerationsWithStreamingResponse(self._client.moderations) @cached_property def models(self) -> models.AsyncModelsWithStreamingResponse: + """List and describe the various models available in the API.""" from .resources.models import AsyncModelsWithStreamingResponse return AsyncModelsWithStreamingResponse(self._client.models) @@ -1252,12 +1356,14 @@ def beta(self) -> beta.AsyncBetaWithStreamingResponse: @cached_property def batches(self) -> batches.AsyncBatchesWithStreamingResponse: + """Create large batches of API requests to run asynchronously.""" from .resources.batches import AsyncBatchesWithStreamingResponse return AsyncBatchesWithStreamingResponse(self._client.batches) @cached_property def uploads(self) -> uploads.AsyncUploadsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" from .resources.uploads import AsyncUploadsWithStreamingResponse return AsyncUploadsWithStreamingResponse(self._client.uploads) @@ -1276,12 +1382,14 @@ def realtime(self) -> realtime.AsyncRealtimeWithStreamingResponse: @cached_property def conversations(self) -> conversations.AsyncConversationsWithStreamingResponse: + """Manage conversations and conversation items.""" from .resources.conversations import AsyncConversationsWithStreamingResponse return AsyncConversationsWithStreamingResponse(self._client.conversations) @cached_property def evals(self) -> evals.AsyncEvalsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" from .resources.evals import AsyncEvalsWithStreamingResponse return AsyncEvalsWithStreamingResponse(self._client.evals) diff --git a/src/openai/_version.py b/src/openai/_version.py index 08cf29390a..417b40c283 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.24.0" # x-release-please-version +__version__ = "2.25.0" # x-release-please-version diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 5676eb0b63..df0f52cdc8 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -101,6 +101,8 @@ def parse_response( output.type == "computer_call" or output.type == "file_search_call" or output.type == "web_search_call" + or output.type == "tool_search_call" + or output.type == "tool_search_output" or output.type == "reasoning" or output.type == "compaction" or output.type == "mcp_call" diff --git a/src/openai/resources/audio/audio.py b/src/openai/resources/audio/audio.py index 383b7073bf..040a058df6 100644 --- a/src/openai/resources/audio/audio.py +++ b/src/openai/resources/audio/audio.py @@ -35,14 +35,17 @@ class Audio(SyncAPIResource): @cached_property def transcriptions(self) -> Transcriptions: + """Turn audio into text or text into audio.""" return Transcriptions(self._client) @cached_property def translations(self) -> Translations: + """Turn audio into text or text into audio.""" return Translations(self._client) @cached_property def speech(self) -> Speech: + """Turn audio into text or text into audio.""" return Speech(self._client) @cached_property @@ -68,14 +71,17 @@ def with_streaming_response(self) -> AudioWithStreamingResponse: class AsyncAudio(AsyncAPIResource): @cached_property def transcriptions(self) -> AsyncTranscriptions: + """Turn audio into text or text into audio.""" return AsyncTranscriptions(self._client) @cached_property def translations(self) -> AsyncTranslations: + """Turn audio into text or text into audio.""" return AsyncTranslations(self._client) @cached_property def speech(self) -> AsyncSpeech: + """Turn audio into text or text into audio.""" return AsyncSpeech(self._client) @cached_property @@ -104,14 +110,17 @@ def __init__(self, audio: Audio) -> None: @cached_property def transcriptions(self) -> TranscriptionsWithRawResponse: + """Turn audio into text or text into audio.""" return TranscriptionsWithRawResponse(self._audio.transcriptions) @cached_property def translations(self) -> TranslationsWithRawResponse: + """Turn audio into text or text into audio.""" return TranslationsWithRawResponse(self._audio.translations) @cached_property def speech(self) -> SpeechWithRawResponse: + """Turn audio into text or text into audio.""" return SpeechWithRawResponse(self._audio.speech) @@ -121,14 +130,17 @@ def __init__(self, audio: AsyncAudio) -> None: @cached_property def transcriptions(self) -> AsyncTranscriptionsWithRawResponse: + """Turn audio into text or text into audio.""" return AsyncTranscriptionsWithRawResponse(self._audio.transcriptions) @cached_property def translations(self) -> AsyncTranslationsWithRawResponse: + """Turn audio into text or text into audio.""" return AsyncTranslationsWithRawResponse(self._audio.translations) @cached_property def speech(self) -> AsyncSpeechWithRawResponse: + """Turn audio into text or text into audio.""" return AsyncSpeechWithRawResponse(self._audio.speech) @@ -138,14 +150,17 @@ def __init__(self, audio: Audio) -> None: @cached_property def transcriptions(self) -> TranscriptionsWithStreamingResponse: + """Turn audio into text or text into audio.""" return TranscriptionsWithStreamingResponse(self._audio.transcriptions) @cached_property def translations(self) -> TranslationsWithStreamingResponse: + """Turn audio into text or text into audio.""" return TranslationsWithStreamingResponse(self._audio.translations) @cached_property def speech(self) -> SpeechWithStreamingResponse: + """Turn audio into text or text into audio.""" return SpeechWithStreamingResponse(self._audio.speech) @@ -155,12 +170,15 @@ def __init__(self, audio: AsyncAudio) -> None: @cached_property def transcriptions(self) -> AsyncTranscriptionsWithStreamingResponse: + """Turn audio into text or text into audio.""" return AsyncTranscriptionsWithStreamingResponse(self._audio.transcriptions) @cached_property def translations(self) -> AsyncTranslationsWithStreamingResponse: + """Turn audio into text or text into audio.""" return AsyncTranslationsWithStreamingResponse(self._audio.translations) @cached_property def speech(self) -> AsyncSpeechWithStreamingResponse: + """Turn audio into text or text into audio.""" return AsyncSpeechWithStreamingResponse(self._audio.speech) diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 96a32f9268..f937321baa 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -26,6 +26,8 @@ class Speech(SyncAPIResource): + """Turn audio into text or text into audio.""" + @cached_property def with_raw_response(self) -> SpeechWithRawResponse: """ @@ -125,6 +127,8 @@ def create( class AsyncSpeech(AsyncAPIResource): + """Turn audio into text or text into audio.""" + @cached_property def with_raw_response(self) -> AsyncSpeechWithRawResponse: """ diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index bc6e9f22de..25e6e0cb5e 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -42,6 +42,8 @@ class Transcriptions(SyncAPIResource): + """Turn audio into text or text into audio.""" + @cached_property def with_raw_response(self) -> TranscriptionsWithRawResponse: """ @@ -497,6 +499,8 @@ def create( class AsyncTranscriptions(AsyncAPIResource): + """Turn audio into text or text into audio.""" + @cached_property def with_raw_response(self) -> AsyncTranscriptionsWithRawResponse: """ diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 310f901fb3..0751a65586 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -27,6 +27,8 @@ class Translations(SyncAPIResource): + """Turn audio into text or text into audio.""" + @cached_property def with_raw_response(self) -> TranslationsWithRawResponse: """ @@ -170,6 +172,8 @@ def create( class AsyncTranslations(AsyncAPIResource): + """Turn audio into text or text into audio.""" + @cached_property def with_raw_response(self) -> AsyncTranslationsWithRawResponse: """ diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index bc856bf5aa..005a32870e 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -23,6 +23,8 @@ class Batches(SyncAPIResource): + """Create large batches of API requests to run asynchronously.""" + @cached_property def with_raw_response(self) -> BatchesWithRawResponse: """ @@ -247,6 +249,8 @@ def cancel( class AsyncBatches(AsyncAPIResource): + """Create large batches of API requests to run asynchronously.""" + @cached_property def with_raw_response(self) -> AsyncBatchesWithRawResponse: """ diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 8c69700059..bf22122553 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -33,6 +33,8 @@ class Assistants(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def with_raw_response(self) -> AssistantsWithRawResponse: """ @@ -507,6 +509,8 @@ def delete( class AsyncAssistants(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def with_raw_response(self) -> AsyncAssistantsWithRawResponse: """ diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 5ee3639db1..388a1c5d1f 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -52,10 +52,12 @@ def chatkit(self) -> ChatKit: @cached_property def assistants(self) -> Assistants: + """Build Assistants that can call models and use tools.""" return Assistants(self._client) @cached_property def threads(self) -> Threads: + """Build Assistants that can call models and use tools.""" return Threads(self._client) @cached_property @@ -93,10 +95,12 @@ def chatkit(self) -> AsyncChatKit: @cached_property def assistants(self) -> AsyncAssistants: + """Build Assistants that can call models and use tools.""" return AsyncAssistants(self._client) @cached_property def threads(self) -> AsyncThreads: + """Build Assistants that can call models and use tools.""" return AsyncThreads(self._client) @cached_property @@ -129,10 +133,12 @@ def chatkit(self) -> ChatKitWithRawResponse: @cached_property def assistants(self) -> AssistantsWithRawResponse: + """Build Assistants that can call models and use tools.""" return AssistantsWithRawResponse(self._beta.assistants) @cached_property def threads(self) -> ThreadsWithRawResponse: + """Build Assistants that can call models and use tools.""" return ThreadsWithRawResponse(self._beta.threads) @@ -146,10 +152,12 @@ def chatkit(self) -> AsyncChatKitWithRawResponse: @cached_property def assistants(self) -> AsyncAssistantsWithRawResponse: + """Build Assistants that can call models and use tools.""" return AsyncAssistantsWithRawResponse(self._beta.assistants) @cached_property def threads(self) -> AsyncThreadsWithRawResponse: + """Build Assistants that can call models and use tools.""" return AsyncThreadsWithRawResponse(self._beta.threads) @@ -163,10 +171,12 @@ def chatkit(self) -> ChatKitWithStreamingResponse: @cached_property def assistants(self) -> AssistantsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return AssistantsWithStreamingResponse(self._beta.assistants) @cached_property def threads(self) -> ThreadsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return ThreadsWithStreamingResponse(self._beta.threads) @@ -180,8 +190,10 @@ def chatkit(self) -> AsyncChatKitWithStreamingResponse: @cached_property def assistants(self) -> AsyncAssistantsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return AsyncAssistantsWithStreamingResponse(self._beta.assistants) @cached_property def threads(self) -> AsyncThreadsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return AsyncThreadsWithStreamingResponse(self._beta.threads) diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index d94ecca9a2..e783310933 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -29,6 +29,8 @@ class Messages(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def with_raw_response(self) -> MessagesWithRawResponse: """ @@ -312,6 +314,8 @@ def delete( class AsyncMessages(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: """ diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 90845a2f62..20862185d2 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -59,8 +59,11 @@ class Runs(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def steps(self) -> Steps: + """Build Assistants that can call models and use tools.""" return Steps(self._client) @cached_property @@ -1518,8 +1521,11 @@ def submit_tool_outputs_stream( class AsyncRuns(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def steps(self) -> AsyncSteps: + """Build Assistants that can call models and use tools.""" return AsyncSteps(self._client) @cached_property @@ -3015,6 +3021,7 @@ def __init__(self, runs: Runs) -> None: @cached_property def steps(self) -> StepsWithRawResponse: + """Build Assistants that can call models and use tools.""" return StepsWithRawResponse(self._runs.steps) @@ -3055,6 +3062,7 @@ def __init__(self, runs: AsyncRuns) -> None: @cached_property def steps(self) -> AsyncStepsWithRawResponse: + """Build Assistants that can call models and use tools.""" return AsyncStepsWithRawResponse(self._runs.steps) @@ -3095,6 +3103,7 @@ def __init__(self, runs: Runs) -> None: @cached_property def steps(self) -> StepsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return StepsWithStreamingResponse(self._runs.steps) @@ -3135,4 +3144,5 @@ def __init__(self, runs: AsyncRuns) -> None: @cached_property def steps(self) -> AsyncStepsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return AsyncStepsWithStreamingResponse(self._runs.steps) diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 254a94435c..dea5df69bc 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -24,6 +24,8 @@ class Steps(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def with_raw_response(self) -> StepsWithRawResponse: """ @@ -180,6 +182,8 @@ def list( class AsyncSteps(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def with_raw_response(self) -> AsyncStepsWithRawResponse: """ diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index a804fda159..0a93baf452 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -60,12 +60,16 @@ class Threads(SyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def runs(self) -> Runs: + """Build Assistants that can call models and use tools.""" return Runs(self._client) @cached_property def messages(self) -> Messages: + """Build Assistants that can call models and use tools.""" return Messages(self._client) @cached_property @@ -922,12 +926,16 @@ def create_and_run_stream( class AsyncThreads(AsyncAPIResource): + """Build Assistants that can call models and use tools.""" + @cached_property def runs(self) -> AsyncRuns: + """Build Assistants that can call models and use tools.""" return AsyncRuns(self._client) @cached_property def messages(self) -> AsyncMessages: + """Build Assistants that can call models and use tools.""" return AsyncMessages(self._client) @cached_property @@ -1819,10 +1827,12 @@ def __init__(self, threads: Threads) -> None: @cached_property def runs(self) -> RunsWithRawResponse: + """Build Assistants that can call models and use tools.""" return RunsWithRawResponse(self._threads.runs) @cached_property def messages(self) -> MessagesWithRawResponse: + """Build Assistants that can call models and use tools.""" return MessagesWithRawResponse(self._threads.messages) @@ -1858,10 +1868,12 @@ def __init__(self, threads: AsyncThreads) -> None: @cached_property def runs(self) -> AsyncRunsWithRawResponse: + """Build Assistants that can call models and use tools.""" return AsyncRunsWithRawResponse(self._threads.runs) @cached_property def messages(self) -> AsyncMessagesWithRawResponse: + """Build Assistants that can call models and use tools.""" return AsyncMessagesWithRawResponse(self._threads.messages) @@ -1897,10 +1909,12 @@ def __init__(self, threads: Threads) -> None: @cached_property def runs(self) -> RunsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return RunsWithStreamingResponse(self._threads.runs) @cached_property def messages(self) -> MessagesWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return MessagesWithStreamingResponse(self._threads.messages) @@ -1936,8 +1950,10 @@ def __init__(self, threads: AsyncThreads) -> None: @cached_property def runs(self) -> AsyncRunsWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return AsyncRunsWithStreamingResponse(self._threads.runs) @cached_property def messages(self) -> AsyncMessagesWithStreamingResponse: + """Build Assistants that can call models and use tools.""" return AsyncMessagesWithStreamingResponse(self._threads.messages) diff --git a/src/openai/resources/chat/chat.py b/src/openai/resources/chat/chat.py index 14f9224b41..2c921e7480 100644 --- a/src/openai/resources/chat/chat.py +++ b/src/openai/resources/chat/chat.py @@ -19,6 +19,9 @@ class Chat(SyncAPIResource): @cached_property def completions(self) -> Completions: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return Completions(self._client) @cached_property @@ -44,6 +47,9 @@ def with_streaming_response(self) -> ChatWithStreamingResponse: class AsyncChat(AsyncAPIResource): @cached_property def completions(self) -> AsyncCompletions: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return AsyncCompletions(self._client) @cached_property @@ -72,6 +78,9 @@ def __init__(self, chat: Chat) -> None: @cached_property def completions(self) -> CompletionsWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return CompletionsWithRawResponse(self._chat.completions) @@ -81,6 +90,9 @@ def __init__(self, chat: AsyncChat) -> None: @cached_property def completions(self) -> AsyncCompletionsWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return AsyncCompletionsWithRawResponse(self._chat.completions) @@ -90,6 +102,9 @@ def __init__(self, chat: Chat) -> None: @cached_property def completions(self) -> CompletionsWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return CompletionsWithStreamingResponse(self._chat.completions) @@ -99,4 +114,7 @@ def __init__(self, chat: AsyncChat) -> None: @cached_property def completions(self) -> AsyncCompletionsWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return AsyncCompletionsWithStreamingResponse(self._chat.completions) diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 5d56d05d87..a705c1f658 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -58,8 +58,15 @@ class Completions(SyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + @cached_property def messages(self) -> Messages: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return Messages(self._client) @cached_property @@ -1554,8 +1561,15 @@ def stream( class AsyncCompletions(AsyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + @cached_property def messages(self) -> AsyncMessages: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return AsyncMessages(self._client) @cached_property @@ -3075,6 +3089,9 @@ def __init__(self, completions: Completions) -> None: @cached_property def messages(self) -> MessagesWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return MessagesWithRawResponse(self._completions.messages) @@ -3103,6 +3120,9 @@ def __init__(self, completions: AsyncCompletions) -> None: @cached_property def messages(self) -> AsyncMessagesWithRawResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return AsyncMessagesWithRawResponse(self._completions.messages) @@ -3131,6 +3151,9 @@ def __init__(self, completions: Completions) -> None: @cached_property def messages(self) -> MessagesWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return MessagesWithStreamingResponse(self._completions.messages) @@ -3159,6 +3182,9 @@ def __init__(self, completions: AsyncCompletions) -> None: @cached_property def messages(self) -> AsyncMessagesWithStreamingResponse: + """ + Given a list of messages comprising a conversation, the model will return a response. + """ return AsyncMessagesWithStreamingResponse(self._completions.messages) diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py index 3d6dc79cd6..b1c6a08d51 100644 --- a/src/openai/resources/chat/completions/messages.py +++ b/src/openai/resources/chat/completions/messages.py @@ -21,6 +21,10 @@ class Messages(SyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + @cached_property def with_raw_response(self) -> MessagesWithRawResponse: """ @@ -99,6 +103,10 @@ def list( class AsyncMessages(AsyncAPIResource): + """ + Given a list of messages comprising a conversation, the model will return a response. + """ + @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: """ diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 4b6e29395b..4c9e266787 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -25,6 +25,10 @@ class Completions(SyncAPIResource): + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + @cached_property def with_raw_response(self) -> CompletionsWithRawResponse: """ @@ -584,6 +588,10 @@ def create( class AsyncCompletions(AsyncAPIResource): + """ + Given a prompt, the model will return one or more predicted completions, and can also return the probabilities of alternative tokens at each position. + """ + @cached_property def with_raw_response(self) -> AsyncCompletionsWithRawResponse: """ diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py index da037a4e22..f2c54e4d04 100644 --- a/src/openai/resources/conversations/conversations.py +++ b/src/openai/resources/conversations/conversations.py @@ -31,8 +31,11 @@ class Conversations(SyncAPIResource): + """Manage conversations and conversation items.""" + @cached_property def items(self) -> Items: + """Manage conversations and conversation items.""" return Items(self._client) @cached_property @@ -214,8 +217,11 @@ def delete( class AsyncConversations(AsyncAPIResource): + """Manage conversations and conversation items.""" + @cached_property def items(self) -> AsyncItems: + """Manage conversations and conversation items.""" return AsyncItems(self._client) @cached_property @@ -417,6 +423,7 @@ def __init__(self, conversations: Conversations) -> None: @cached_property def items(self) -> ItemsWithRawResponse: + """Manage conversations and conversation items.""" return ItemsWithRawResponse(self._conversations.items) @@ -439,6 +446,7 @@ def __init__(self, conversations: AsyncConversations) -> None: @cached_property def items(self) -> AsyncItemsWithRawResponse: + """Manage conversations and conversation items.""" return AsyncItemsWithRawResponse(self._conversations.items) @@ -461,6 +469,7 @@ def __init__(self, conversations: Conversations) -> None: @cached_property def items(self) -> ItemsWithStreamingResponse: + """Manage conversations and conversation items.""" return ItemsWithStreamingResponse(self._conversations.items) @@ -483,4 +492,5 @@ def __init__(self, conversations: AsyncConversations) -> None: @cached_property def items(self) -> AsyncItemsWithStreamingResponse: + """Manage conversations and conversation items.""" return AsyncItemsWithStreamingResponse(self._conversations.items) diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py index 3dba144849..1f8e101f7f 100644 --- a/src/openai/resources/conversations/items.py +++ b/src/openai/resources/conversations/items.py @@ -26,6 +26,8 @@ class Items(SyncAPIResource): + """Manage conversations and conversation items.""" + @cached_property def with_raw_response(self) -> ItemsWithRawResponse: """ @@ -256,6 +258,8 @@ def delete( class AsyncItems(AsyncAPIResource): + """Manage conversations and conversation items.""" + @cached_property def with_raw_response(self) -> AsyncItemsWithRawResponse: """ diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 5dc3dfa9b3..86eb949a40 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -25,6 +25,10 @@ class Embeddings(SyncAPIResource): + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + @cached_property def with_raw_response(self) -> EmbeddingsWithRawResponse: """ @@ -144,6 +148,10 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: class AsyncEmbeddings(AsyncAPIResource): + """ + Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + """ + @cached_property def with_raw_response(self) -> AsyncEmbeddingsWithRawResponse: """ diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py index 40c4a3e9a3..f0fe28fe8c 100644 --- a/src/openai/resources/evals/evals.py +++ b/src/openai/resources/evals/evals.py @@ -35,8 +35,11 @@ class Evals(SyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + @cached_property def runs(self) -> Runs: + """Manage and run evals in the OpenAI platform.""" return Runs(self._client) @cached_property @@ -299,8 +302,11 @@ def delete( class AsyncEvals(AsyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + @cached_property def runs(self) -> AsyncRuns: + """Manage and run evals in the OpenAI platform.""" return AsyncRuns(self._client) @cached_property @@ -584,6 +590,7 @@ def __init__(self, evals: Evals) -> None: @cached_property def runs(self) -> RunsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" return RunsWithRawResponse(self._evals.runs) @@ -609,6 +616,7 @@ def __init__(self, evals: AsyncEvals) -> None: @cached_property def runs(self) -> AsyncRunsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" return AsyncRunsWithRawResponse(self._evals.runs) @@ -634,6 +642,7 @@ def __init__(self, evals: Evals) -> None: @cached_property def runs(self) -> RunsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" return RunsWithStreamingResponse(self._evals.runs) @@ -659,4 +668,5 @@ def __init__(self, evals: AsyncEvals) -> None: @cached_property def runs(self) -> AsyncRunsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" return AsyncRunsWithStreamingResponse(self._evals.runs) diff --git a/src/openai/resources/evals/runs/output_items.py b/src/openai/resources/evals/runs/output_items.py index c2dee72122..c2e6647715 100644 --- a/src/openai/resources/evals/runs/output_items.py +++ b/src/openai/resources/evals/runs/output_items.py @@ -22,6 +22,8 @@ class OutputItems(SyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + @cached_property def with_raw_response(self) -> OutputItemsWithRawResponse: """ @@ -145,6 +147,8 @@ def list( class AsyncOutputItems(AsyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + @cached_property def with_raw_response(self) -> AsyncOutputItemsWithRawResponse: """ diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py index b747b198f8..49eecd768f 100644 --- a/src/openai/resources/evals/runs/runs.py +++ b/src/openai/resources/evals/runs/runs.py @@ -35,8 +35,11 @@ class Runs(SyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + @cached_property def output_items(self) -> OutputItems: + """Manage and run evals in the OpenAI platform.""" return OutputItems(self._client) @cached_property @@ -285,8 +288,11 @@ def cancel( class AsyncRuns(AsyncAPIResource): + """Manage and run evals in the OpenAI platform.""" + @cached_property def output_items(self) -> AsyncOutputItems: + """Manage and run evals in the OpenAI platform.""" return AsyncOutputItems(self._client) @cached_property @@ -556,6 +562,7 @@ def __init__(self, runs: Runs) -> None: @cached_property def output_items(self) -> OutputItemsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" return OutputItemsWithRawResponse(self._runs.output_items) @@ -581,6 +588,7 @@ def __init__(self, runs: AsyncRuns) -> None: @cached_property def output_items(self) -> AsyncOutputItemsWithRawResponse: + """Manage and run evals in the OpenAI platform.""" return AsyncOutputItemsWithRawResponse(self._runs.output_items) @@ -606,6 +614,7 @@ def __init__(self, runs: Runs) -> None: @cached_property def output_items(self) -> OutputItemsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" return OutputItemsWithStreamingResponse(self._runs.output_items) @@ -631,4 +640,5 @@ def __init__(self, runs: AsyncRuns) -> None: @cached_property def output_items(self) -> AsyncOutputItemsWithStreamingResponse: + """Manage and run evals in the OpenAI platform.""" return AsyncOutputItemsWithStreamingResponse(self._runs.output_items) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 964d6505e7..7341b326dc 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -33,6 +33,10 @@ class Files(SyncAPIResource): + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + @cached_property def with_raw_response(self) -> FilesWithRawResponse: """ @@ -354,6 +358,10 @@ def wait_for_processing( class AsyncFiles(AsyncAPIResource): + """ + Files are used to upload documents that can be used with features like Assistants and Fine-tuning. + """ + @cached_property def with_raw_response(self) -> AsyncFilesWithRawResponse: """ diff --git a/src/openai/resources/fine_tuning/alpha/alpha.py b/src/openai/resources/fine_tuning/alpha/alpha.py index 54c05fab69..183208d0ab 100644 --- a/src/openai/resources/fine_tuning/alpha/alpha.py +++ b/src/openai/resources/fine_tuning/alpha/alpha.py @@ -19,6 +19,7 @@ class Alpha(SyncAPIResource): @cached_property def graders(self) -> Graders: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return Graders(self._client) @cached_property @@ -44,6 +45,7 @@ def with_streaming_response(self) -> AlphaWithStreamingResponse: class AsyncAlpha(AsyncAPIResource): @cached_property def graders(self) -> AsyncGraders: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncGraders(self._client) @cached_property @@ -72,6 +74,7 @@ def __init__(self, alpha: Alpha) -> None: @cached_property def graders(self) -> GradersWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return GradersWithRawResponse(self._alpha.graders) @@ -81,6 +84,7 @@ def __init__(self, alpha: AsyncAlpha) -> None: @cached_property def graders(self) -> AsyncGradersWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncGradersWithRawResponse(self._alpha.graders) @@ -90,6 +94,7 @@ def __init__(self, alpha: Alpha) -> None: @cached_property def graders(self) -> GradersWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return GradersWithStreamingResponse(self._alpha.graders) @@ -99,4 +104,5 @@ def __init__(self, alpha: AsyncAlpha) -> None: @cached_property def graders(self) -> AsyncGradersWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncGradersWithStreamingResponse(self._alpha.graders) diff --git a/src/openai/resources/fine_tuning/alpha/graders.py b/src/openai/resources/fine_tuning/alpha/graders.py index e7a9b925ea..e5d5dea5de 100644 --- a/src/openai/resources/fine_tuning/alpha/graders.py +++ b/src/openai/resources/fine_tuning/alpha/graders.py @@ -19,6 +19,8 @@ class Graders(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def with_raw_response(self) -> GradersWithRawResponse: """ @@ -127,6 +129,8 @@ def validate( class AsyncGraders(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def with_raw_response(self) -> AsyncGradersWithRawResponse: """ diff --git a/src/openai/resources/fine_tuning/checkpoints/checkpoints.py b/src/openai/resources/fine_tuning/checkpoints/checkpoints.py index f59976a264..9c2ed6f576 100644 --- a/src/openai/resources/fine_tuning/checkpoints/checkpoints.py +++ b/src/openai/resources/fine_tuning/checkpoints/checkpoints.py @@ -19,6 +19,7 @@ class Checkpoints(SyncAPIResource): @cached_property def permissions(self) -> Permissions: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return Permissions(self._client) @cached_property @@ -44,6 +45,7 @@ def with_streaming_response(self) -> CheckpointsWithStreamingResponse: class AsyncCheckpoints(AsyncAPIResource): @cached_property def permissions(self) -> AsyncPermissions: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncPermissions(self._client) @cached_property @@ -72,6 +74,7 @@ def __init__(self, checkpoints: Checkpoints) -> None: @cached_property def permissions(self) -> PermissionsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return PermissionsWithRawResponse(self._checkpoints.permissions) @@ -81,6 +84,7 @@ def __init__(self, checkpoints: AsyncCheckpoints) -> None: @cached_property def permissions(self) -> AsyncPermissionsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncPermissionsWithRawResponse(self._checkpoints.permissions) @@ -90,6 +94,7 @@ def __init__(self, checkpoints: Checkpoints) -> None: @cached_property def permissions(self) -> PermissionsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return PermissionsWithStreamingResponse(self._checkpoints.permissions) @@ -99,4 +104,5 @@ def __init__(self, checkpoints: AsyncCheckpoints) -> None: @cached_property def permissions(self) -> AsyncPermissionsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncPermissionsWithStreamingResponse(self._checkpoints.permissions) diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py index e7f55b82d9..35e06feee0 100644 --- a/src/openai/resources/fine_tuning/checkpoints/permissions.py +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -2,6 +2,7 @@ from __future__ import annotations +import typing_extensions from typing_extensions import Literal import httpx @@ -12,9 +13,14 @@ from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPage, AsyncPage +from ....pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage from ...._base_client import AsyncPaginator, make_request_options -from ....types.fine_tuning.checkpoints import permission_create_params, permission_retrieve_params +from ....types.fine_tuning.checkpoints import ( + permission_list_params, + permission_create_params, + permission_retrieve_params, +) +from ....types.fine_tuning.checkpoints.permission_list_response import PermissionListResponse from ....types.fine_tuning.checkpoints.permission_create_response import PermissionCreateResponse from ....types.fine_tuning.checkpoints.permission_delete_response import PermissionDeleteResponse from ....types.fine_tuning.checkpoints.permission_retrieve_response import PermissionRetrieveResponse @@ -23,6 +29,8 @@ class Permissions(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def with_raw_response(self) -> PermissionsWithRawResponse: """ @@ -86,6 +94,7 @@ def create( method="post", ) + @typing_extensions.deprecated("Retrieve is deprecated. Please swap to the paginated list method instead.") def retrieve( self, fine_tuned_model_checkpoint: str, @@ -148,6 +157,69 @@ def retrieve( cast_to=PermissionRetrieveResponse, ) + def list( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[PermissionListResponse]: + """ + **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + page=SyncConversationCursorPage[PermissionListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_list_params.PermissionListParams, + ), + ), + model=PermissionListResponse, + ) + def delete( self, permission_id: str, @@ -191,6 +263,8 @@ def delete( class AsyncPermissions(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def with_raw_response(self) -> AsyncPermissionsWithRawResponse: """ @@ -254,6 +328,7 @@ def create( method="post", ) + @typing_extensions.deprecated("Retrieve is deprecated. Please swap to the paginated list method instead.") async def retrieve( self, fine_tuned_model_checkpoint: str, @@ -316,6 +391,69 @@ async def retrieve( cast_to=PermissionRetrieveResponse, ) + def list( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[PermissionListResponse, AsyncConversationCursorPage[PermissionListResponse]]: + """ + **NOTE:** This endpoint requires an [admin API key](../admin-api-keys). + + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + after: Identifier for the last permission ID from the previous pagination request. + + limit: Number of permissions to retrieve. + + order: The order in which to retrieve permissions. + + project_id: The ID of the project to get permissions for. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not fine_tuned_model_checkpoint: + raise ValueError( + f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" + ) + return self._get_api_list( + f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + page=AsyncConversationCursorPage[PermissionListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + "project_id": project_id, + }, + permission_list_params.PermissionListParams, + ), + ), + model=PermissionListResponse, + ) + async def delete( self, permission_id: str, @@ -365,8 +503,13 @@ def __init__(self, permissions: Permissions) -> None: self.create = _legacy_response.to_raw_response_wrapper( permissions.create, ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - permissions.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.to_raw_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = _legacy_response.to_raw_response_wrapper( + permissions.list, ) self.delete = _legacy_response.to_raw_response_wrapper( permissions.delete, @@ -380,8 +523,13 @@ def __init__(self, permissions: AsyncPermissions) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( permissions.create, ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - permissions.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + _legacy_response.async_to_raw_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + permissions.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( permissions.delete, @@ -395,8 +543,13 @@ def __init__(self, permissions: Permissions) -> None: self.create = to_streamed_response_wrapper( permissions.create, ) - self.retrieve = to_streamed_response_wrapper( - permissions.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + to_streamed_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = to_streamed_response_wrapper( + permissions.list, ) self.delete = to_streamed_response_wrapper( permissions.delete, @@ -410,8 +563,13 @@ def __init__(self, permissions: AsyncPermissions) -> None: self.create = async_to_streamed_response_wrapper( permissions.create, ) - self.retrieve = async_to_streamed_response_wrapper( - permissions.retrieve, + self.retrieve = ( # pyright: ignore[reportDeprecated] + async_to_streamed_response_wrapper( + permissions.retrieve, # pyright: ignore[reportDeprecated], + ) + ) + self.list = async_to_streamed_response_wrapper( + permissions.list, ) self.delete = async_to_streamed_response_wrapper( permissions.delete, diff --git a/src/openai/resources/fine_tuning/fine_tuning.py b/src/openai/resources/fine_tuning/fine_tuning.py index 25ae3e8cf4..60f1f44bdc 100644 --- a/src/openai/resources/fine_tuning/fine_tuning.py +++ b/src/openai/resources/fine_tuning/fine_tuning.py @@ -35,6 +35,7 @@ class FineTuning(SyncAPIResource): @cached_property def jobs(self) -> Jobs: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return Jobs(self._client) @cached_property @@ -68,6 +69,7 @@ def with_streaming_response(self) -> FineTuningWithStreamingResponse: class AsyncFineTuning(AsyncAPIResource): @cached_property def jobs(self) -> AsyncJobs: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncJobs(self._client) @cached_property @@ -104,6 +106,7 @@ def __init__(self, fine_tuning: FineTuning) -> None: @cached_property def jobs(self) -> JobsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return JobsWithRawResponse(self._fine_tuning.jobs) @cached_property @@ -121,6 +124,7 @@ def __init__(self, fine_tuning: AsyncFineTuning) -> None: @cached_property def jobs(self) -> AsyncJobsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncJobsWithRawResponse(self._fine_tuning.jobs) @cached_property @@ -138,6 +142,7 @@ def __init__(self, fine_tuning: FineTuning) -> None: @cached_property def jobs(self) -> JobsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return JobsWithStreamingResponse(self._fine_tuning.jobs) @cached_property @@ -155,6 +160,7 @@ def __init__(self, fine_tuning: AsyncFineTuning) -> None: @cached_property def jobs(self) -> AsyncJobsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncJobsWithStreamingResponse(self._fine_tuning.jobs) @cached_property diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py index f65856f0c6..6f14a0994e 100644 --- a/src/openai/resources/fine_tuning/jobs/checkpoints.py +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -22,6 +22,8 @@ class Checkpoints(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def with_raw_response(self) -> CheckpointsWithRawResponse: """ @@ -93,6 +95,8 @@ def list( class AsyncCheckpoints(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def with_raw_response(self) -> AsyncCheckpointsWithRawResponse: """ diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index b292e057cf..e38baa5539 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -35,8 +35,11 @@ class Jobs(SyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def checkpoints(self) -> Checkpoints: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return Checkpoints(self._client) @cached_property @@ -415,8 +418,11 @@ def resume( class AsyncJobs(AsyncAPIResource): + """Manage fine-tuning jobs to tailor a model to your specific training data.""" + @cached_property def checkpoints(self) -> AsyncCheckpoints: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncCheckpoints(self._client) @cached_property @@ -822,6 +828,7 @@ def __init__(self, jobs: Jobs) -> None: @cached_property def checkpoints(self) -> CheckpointsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return CheckpointsWithRawResponse(self._jobs.checkpoints) @@ -853,6 +860,7 @@ def __init__(self, jobs: AsyncJobs) -> None: @cached_property def checkpoints(self) -> AsyncCheckpointsWithRawResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncCheckpointsWithRawResponse(self._jobs.checkpoints) @@ -884,6 +892,7 @@ def __init__(self, jobs: Jobs) -> None: @cached_property def checkpoints(self) -> CheckpointsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return CheckpointsWithStreamingResponse(self._jobs.checkpoints) @@ -915,4 +924,5 @@ def __init__(self, jobs: AsyncJobs) -> None: @cached_property def checkpoints(self) -> AsyncCheckpointsWithStreamingResponse: + """Manage fine-tuning jobs to tailor a model to your specific training data.""" return AsyncCheckpointsWithStreamingResponse(self._jobs.checkpoints) diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 647eb1ca24..6959c2aeff 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -25,6 +25,8 @@ class Images(SyncAPIResource): + """Given a prompt and/or an input image, the model will generate a new image.""" + @cached_property def with_raw_response(self) -> ImagesWithRawResponse: """ @@ -915,6 +917,8 @@ def generate( class AsyncImages(AsyncAPIResource): + """Given a prompt and/or an input image, the model will generate a new image.""" + @cached_property def with_raw_response(self) -> AsyncImagesWithRawResponse: """ diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index a8f7691055..508393263f 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -21,6 +21,8 @@ class Models(SyncAPIResource): + """List and describe the various models available in the API.""" + @cached_property def with_raw_response(self) -> ModelsWithRawResponse: """ @@ -134,6 +136,8 @@ def delete( class AsyncModels(AsyncAPIResource): + """List and describe the various models available in the API.""" + @cached_property def with_raw_response(self) -> AsyncModelsWithRawResponse: """ diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 5f378f71e7..0b9a2d23c7 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -22,6 +22,10 @@ class Moderations(SyncAPIResource): + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + @cached_property def with_raw_response(self) -> ModerationsWithRawResponse: """ @@ -92,6 +96,10 @@ def create( class AsyncModerations(AsyncAPIResource): + """ + Given text and/or image inputs, classifies if those inputs are potentially harmful. + """ + @cached_property def with_raw_response(self) -> AsyncModerationsWithRawResponse: """ diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md index 36c95d7b83..d654abb99a 100644 --- a/src/openai/resources/responses/api.md +++ b/src/openai/resources/responses/api.md @@ -6,7 +6,10 @@ Types: from openai.types.responses import ( ApplyPatchTool, CompactedResponse, + ComputerAction, + ComputerActionList, ComputerTool, + ComputerUseTool, ContainerAuto, ContainerNetworkPolicyAllowlist, ContainerNetworkPolicyDisabled, @@ -21,6 +24,7 @@ from openai.types.responses import ( InlineSkillSource, LocalEnvironment, LocalSkill, + NamespaceTool, Response, ResponseApplyPatchToolCall, ResponseApplyPatchToolCallOutput, @@ -123,6 +127,9 @@ from openai.types.responses import ( ResponseTextConfig, ResponseTextDeltaEvent, ResponseTextDoneEvent, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, + ResponseToolSearchOutputItemParam, ResponseUsage, ResponseWebSearchCallCompletedEvent, ResponseWebSearchCallInProgressEvent, @@ -139,6 +146,7 @@ from openai.types.responses import ( ToolChoiceOptions, ToolChoiceShell, ToolChoiceTypes, + ToolSearchTool, WebSearchPreviewTool, WebSearchTool, ) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index c85a94495d..5d34909fd1 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1567,6 +1567,8 @@ def compact( *, model: Union[ Literal[ + "gpt-5.4", + "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", "gpt-5.2-chat-latest", @@ -3232,6 +3234,8 @@ async def compact( *, model: Union[ Literal[ + "gpt-5.4", + "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", "gpt-5.2-chat-latest", diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index 73eabd4083..034547f308 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -20,6 +20,8 @@ class Parts(SyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + @cached_property def with_raw_response(self) -> PartsWithRawResponse: """ @@ -95,6 +97,8 @@ def create( class AsyncParts(AsyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + @cached_property def with_raw_response(self) -> AsyncPartsWithRawResponse: """ diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 2873b913ba..f5e5e6f664 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -41,8 +41,11 @@ class Uploads(SyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + @cached_property def parts(self) -> Parts: + """Use Uploads to upload large files in multiple parts.""" return Parts(self._client) @cached_property @@ -343,8 +346,11 @@ def complete( class AsyncUploads(AsyncAPIResource): + """Use Uploads to upload large files in multiple parts.""" + @cached_property def parts(self) -> AsyncParts: + """Use Uploads to upload large files in multiple parts.""" return AsyncParts(self._client) @cached_property @@ -671,6 +677,7 @@ def __init__(self, uploads: Uploads) -> None: @cached_property def parts(self) -> PartsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" return PartsWithRawResponse(self._uploads.parts) @@ -690,6 +697,7 @@ def __init__(self, uploads: AsyncUploads) -> None: @cached_property def parts(self) -> AsyncPartsWithRawResponse: + """Use Uploads to upload large files in multiple parts.""" return AsyncPartsWithRawResponse(self._uploads.parts) @@ -709,6 +717,7 @@ def __init__(self, uploads: Uploads) -> None: @cached_property def parts(self) -> PartsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" return PartsWithStreamingResponse(self._uploads.parts) @@ -728,4 +737,5 @@ def __init__(self, uploads: AsyncUploads) -> None: @cached_property def parts(self) -> AsyncPartsWithStreamingResponse: + """Use Uploads to upload large files in multiple parts.""" return AsyncPartsWithStreamingResponse(self._uploads.parts) diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 85ea79f8bc..51df6da4d3 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -81,7 +81,7 @@ def create( Args: prompt: Text prompt that describes the video to generate. - input_reference: Optional image reference that guides generation. + input_reference: Optional multipart reference asset that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`. @@ -437,7 +437,7 @@ async def create( Args: prompt: Text prompt that describes the video to generate. - input_reference: Optional image reference that guides generation. + input_reference: Optional multipart reference asset that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`. diff --git a/src/openai/resources/webhooks/__init__.py b/src/openai/resources/webhooks/__init__.py index 371906299b..66449ee7e3 100644 --- a/src/openai/resources/webhooks/__init__.py +++ b/src/openai/resources/webhooks/__init__.py @@ -10,4 +10,5 @@ class Webhooks(_Webhooks): class AsyncWebhooks(_AsyncWebhooks): pass + __all__ = ["Webhooks", "AsyncWebhooks"] diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 461d871ab5..5a468a351b 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -187,8 +187,9 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to - add to the vector store. There can be a maximum of 10000 files in a vector - store. + add to the vector store. For vector stores created before Nov 2025, there can be + a maximum of 10,000 files in a vector store. For vector stores created starting + in Nov 2025, the limit is 100,000,000 files. """ metadata: Optional[Metadata] diff --git a/src/openai/types/beta/thread_create_and_run_params.py b/src/openai/types/beta/thread_create_and_run_params.py index c0aee3e9f8..3d3d5d4f2e 100644 --- a/src/openai/types/beta/thread_create_and_run_params.py +++ b/src/openai/types/beta/thread_create_and_run_params.py @@ -274,8 +274,9 @@ class ThreadToolResourcesFileSearchVectorStore(TypedDict, total=False): file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to - add to the vector store. There can be a maximum of 10000 files in a vector - store. + add to the vector store. For vector stores created before Nov 2025, there can be + a maximum of 10,000 files in a vector store. For vector stores created starting + in Nov 2025, the limit is 100,000,000 files. """ metadata: Optional[Metadata] diff --git a/src/openai/types/beta/thread_create_params.py b/src/openai/types/beta/thread_create_params.py index ef83e3d465..b823d1c263 100644 --- a/src/openai/types/beta/thread_create_params.py +++ b/src/openai/types/beta/thread_create_params.py @@ -152,8 +152,9 @@ class ToolResourcesFileSearchVectorStore(TypedDict, total=False): file_ids: SequenceNotStr[str] """ A list of [file](https://platform.openai.com/docs/api-reference/files) IDs to - add to the vector store. There can be a maximum of 10000 files in a vector - store. + add to the vector store. For vector stores created before Nov 2025, there can be + a maximum of 10,000 files in a vector store. For vector stores created starting + in Nov 2025, the limit is 100,000,000 files. """ metadata: Optional[Metadata] diff --git a/src/openai/types/conversations/computer_screenshot_content.py b/src/openai/types/conversations/computer_screenshot_content.py index e42096eba2..ff43a7e589 100644 --- a/src/openai/types/conversations/computer_screenshot_content.py +++ b/src/openai/types/conversations/computer_screenshot_content.py @@ -11,6 +11,12 @@ class ComputerScreenshotContent(BaseModel): """A screenshot of a computer.""" + detail: Literal["low", "high", "auto", "original"] + """The detail level of the screenshot image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + file_id: Optional[str] = None """The identifier of an uploaded file that contains the screenshot.""" diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py index 46268d381c..33bd3ba043 100644 --- a/src/openai/types/conversations/conversation_item.py +++ b/src/openai/types/conversations/conversation_item.py @@ -8,12 +8,14 @@ from ..._models import BaseModel from ..responses.response_reasoning_item import ResponseReasoningItem from ..responses.response_custom_tool_call import ResponseCustomToolCall +from ..responses.response_tool_search_call import ResponseToolSearchCall from ..responses.response_computer_tool_call import ResponseComputerToolCall from ..responses.response_function_web_search import ResponseFunctionWebSearch from ..responses.response_apply_patch_tool_call import ResponseApplyPatchToolCall from ..responses.response_file_search_tool_call import ResponseFileSearchToolCall from ..responses.response_custom_tool_call_output import ResponseCustomToolCallOutput from ..responses.response_function_tool_call_item import ResponseFunctionToolCallItem +from ..responses.response_tool_search_output_item import ResponseToolSearchOutputItem from ..responses.response_function_shell_tool_call import ResponseFunctionShellToolCall from ..responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from ..responses.response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput @@ -229,6 +231,8 @@ class McpCall(BaseModel): ImageGenerationCall, ResponseComputerToolCall, ResponseComputerToolCallOutputItem, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, ResponseReasoningItem, ResponseCodeInterpreterToolCall, LocalShellCall, diff --git a/src/openai/types/fine_tuning/checkpoints/__init__.py b/src/openai/types/fine_tuning/checkpoints/__init__.py index 2947b33145..5447b4d818 100644 --- a/src/openai/types/fine_tuning/checkpoints/__init__.py +++ b/src/openai/types/fine_tuning/checkpoints/__init__.py @@ -2,7 +2,9 @@ from __future__ import annotations +from .permission_list_params import PermissionListParams as PermissionListParams from .permission_create_params import PermissionCreateParams as PermissionCreateParams +from .permission_list_response import PermissionListResponse as PermissionListResponse from .permission_create_response import PermissionCreateResponse as PermissionCreateResponse from .permission_delete_response import PermissionDeleteResponse as PermissionDeleteResponse from .permission_retrieve_params import PermissionRetrieveParams as PermissionRetrieveParams diff --git a/src/openai/types/fine_tuning/checkpoints/permission_list_params.py b/src/openai/types/fine_tuning/checkpoints/permission_list_params.py new file mode 100644 index 0000000000..1f389920aa --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/permission_list_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["PermissionListParams"] + + +class PermissionListParams(TypedDict, total=False): + after: str + """Identifier for the last permission ID from the previous pagination request.""" + + limit: int + """Number of permissions to retrieve.""" + + order: Literal["ascending", "descending"] + """The order in which to retrieve permissions.""" + + project_id: str + """The ID of the project to get permissions for.""" diff --git a/src/openai/types/fine_tuning/checkpoints/permission_list_response.py b/src/openai/types/fine_tuning/checkpoints/permission_list_response.py new file mode 100644 index 0000000000..26e913e0c2 --- /dev/null +++ b/src/openai/types/fine_tuning/checkpoints/permission_list_response.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PermissionListResponse"] + + +class PermissionListResponse(BaseModel): + """ + The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint. + """ + + id: str + """The permission identifier, which can be referenced in the API endpoints.""" + + created_at: int + """The Unix timestamp (in seconds) for when the permission was created.""" + + object: Literal["checkpoint.permission"] + """The object type, which is always "checkpoint.permission".""" + + project_id: str + """The project identifier that the permission is for.""" diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py index 72189e10e6..cb5eae42d4 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py @@ -134,6 +134,9 @@ class RealtimeResponseCreateMcpTool(BaseModel): - SharePoint: `connector_sharepoint` """ + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] = None """Optional HTTP headers to send to the MCP server. diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py index 68dd6bdb5c..dd8c2e018f 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py @@ -134,6 +134,9 @@ class RealtimeResponseCreateMcpToolParam(TypedDict, total=False): - SharePoint: `connector_sharepoint` """ + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] """Optional HTTP headers to send to the MCP server. diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index a74615bd60..3c3bef93f4 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -363,6 +363,9 @@ class ToolMcpTool(BaseModel): - SharePoint: `connector_sharepoint` """ + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] = None """Optional HTTP headers to send to the MCP server. diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py index 3cc404feef..217130922a 100644 --- a/src/openai/types/realtime/realtime_tools_config_param.py +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -137,6 +137,9 @@ class Mcp(TypedDict, total=False): - SharePoint: `connector_sharepoint` """ + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] """Optional HTTP headers to send to the MCP server. diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py index 92aaee7f26..55da58269c 100644 --- a/src/openai/types/realtime/realtime_tools_config_union.py +++ b/src/openai/types/realtime/realtime_tools_config_union.py @@ -137,6 +137,9 @@ class Mcp(BaseModel): - SharePoint: `connector_sharepoint` """ + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] = None """Optional HTTP headers to send to the MCP server. diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py index 6889b4c304..15118f3388 100644 --- a/src/openai/types/realtime/realtime_tools_config_union_param.py +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -136,6 +136,9 @@ class Mcp(TypedDict, total=False): - SharePoint: `connector_sharepoint` """ + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] """Optional HTTP headers to send to the MCP server. diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index de6f68989b..714347b05d 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -12,9 +12,11 @@ from .function_tool import FunctionTool as FunctionTool from .response_item import ResponseItem as ResponseItem from .container_auto import ContainerAuto as ContainerAuto +from .namespace_tool import NamespaceTool as NamespaceTool from .response_error import ResponseError as ResponseError from .response_input import ResponseInput as ResponseInput from .response_usage import ResponseUsage as ResponseUsage +from .computer_action import ComputerAction as ComputerAction from .parsed_response import ( ParsedContent as ParsedContent, ParsedResponse as ParsedResponse, @@ -30,6 +32,8 @@ from .web_search_tool import WebSearchTool as WebSearchTool from .apply_patch_tool import ApplyPatchTool as ApplyPatchTool from .file_search_tool import FileSearchTool as FileSearchTool +from .tool_search_tool import ToolSearchTool as ToolSearchTool +from .computer_use_tool import ComputerUseTool as ComputerUseTool from .custom_tool_param import CustomToolParam as CustomToolParam from .local_environment import LocalEnvironment as LocalEnvironment from .local_skill_param import LocalSkillParam as LocalSkillParam @@ -51,7 +55,9 @@ from .response_input_text import ResponseInputText as ResponseInputText from .tool_choice_allowed import ToolChoiceAllowed as ToolChoiceAllowed from .tool_choice_options import ToolChoiceOptions as ToolChoiceOptions +from .computer_action_list import ComputerActionList as ComputerActionList from .container_auto_param import ContainerAutoParam as ContainerAutoParam +from .namespace_tool_param import NamespaceToolParam as NamespaceToolParam from .response_error_event import ResponseErrorEvent as ResponseErrorEvent from .response_input_audio import ResponseInputAudio as ResponseInputAudio from .response_input_image import ResponseInputImage as ResponseInputImage @@ -60,6 +66,7 @@ from .response_output_text import ResponseOutputText as ResponseOutputText from .response_text_config import ResponseTextConfig as ResponseTextConfig from .tool_choice_function import ToolChoiceFunction as ToolChoiceFunction +from .computer_action_param import ComputerActionParam as ComputerActionParam from .response_failed_event import ResponseFailedEvent as ResponseFailedEvent from .response_prompt_param import ResponsePromptParam as ResponsePromptParam from .response_queued_event import ResponseQueuedEvent as ResponseQueuedEvent @@ -75,6 +82,8 @@ from .response_input_content import ResponseInputContent as ResponseInputContent from .responses_client_event import ResponsesClientEvent as ResponsesClientEvent from .responses_server_event import ResponsesServerEvent as ResponsesServerEvent +from .tool_search_tool_param import ToolSearchToolParam as ToolSearchToolParam +from .computer_use_tool_param import ComputerUseToolParam as ComputerUseToolParam from .local_environment_param import LocalEnvironmentParam as LocalEnvironmentParam from .response_compact_params import ResponseCompactParams as ResponseCompactParams from .response_output_message import ResponseOutputMessage as ResponseOutputMessage @@ -101,7 +110,9 @@ from .response_input_item_param import ResponseInputItemParam as ResponseInputItemParam from .response_input_text_param import ResponseInputTextParam as ResponseInputTextParam from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent +from .response_tool_search_call import ResponseToolSearchCall as ResponseToolSearchCall from .tool_choice_allowed_param import ToolChoiceAllowedParam as ToolChoiceAllowedParam +from .computer_action_list_param import ComputerActionListParam as ComputerActionListParam from .input_token_count_response import InputTokenCountResponse as InputTokenCountResponse from .response_audio_delta_event import ResponseAudioDeltaEvent as ResponseAudioDeltaEvent from .response_in_progress_event import ResponseInProgressEvent as ResponseInProgressEvent @@ -140,6 +151,7 @@ from .response_custom_tool_call_output import ResponseCustomToolCallOutput as ResponseCustomToolCallOutput from .response_function_tool_call_item import ResponseFunctionToolCallItem as ResponseFunctionToolCallItem from .response_output_item_added_event import ResponseOutputItemAddedEvent as ResponseOutputItemAddedEvent +from .response_tool_search_output_item import ResponseToolSearchOutputItem as ResponseToolSearchOutputItem from .container_network_policy_disabled import ContainerNetworkPolicyDisabled as ContainerNetworkPolicyDisabled from .response_computer_tool_call_param import ResponseComputerToolCallParam as ResponseComputerToolCallParam from .response_content_part_added_event import ResponseContentPartAddedEvent as ResponseContentPartAddedEvent @@ -176,6 +188,9 @@ from .response_mcp_call_arguments_done_event import ( ResponseMcpCallArgumentsDoneEvent as ResponseMcpCallArgumentsDoneEvent, ) +from .response_tool_search_output_item_param import ( + ResponseToolSearchOutputItemParam as ResponseToolSearchOutputItemParam, +) from .container_network_policy_disabled_param import ( ContainerNetworkPolicyDisabledParam as ContainerNetworkPolicyDisabledParam, ) @@ -278,6 +293,9 @@ from .response_function_call_arguments_delta_event import ( ResponseFunctionCallArgumentsDeltaEvent as ResponseFunctionCallArgumentsDeltaEvent, ) +from .response_tool_search_output_item_param_param import ( + ResponseToolSearchOutputItemParamParam as ResponseToolSearchOutputItemParamParam, +) from .response_computer_tool_call_output_screenshot import ( ResponseComputerToolCallOutputScreenshot as ResponseComputerToolCallOutputScreenshot, ) diff --git a/src/openai/types/responses/computer_action.py b/src/openai/types/responses/computer_action.py new file mode 100644 index 0000000000..a0c11084ba --- /dev/null +++ b/src/openai/types/responses/computer_action.py @@ -0,0 +1,181 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "ComputerAction", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(BaseModel): + """A click action.""" + + button: Literal["left", "right", "wheel", "back", "forward"] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Literal["click"] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: int + """The x-coordinate where the click occurred.""" + + y: int + """The y-coordinate where the click occurred.""" + + +class DoubleClick(BaseModel): + """A double click action.""" + + type: Literal["double_click"] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: int + """The x-coordinate where the double click occurred.""" + + y: int + """The y-coordinate where the double click occurred.""" + + +class DragPath(BaseModel): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: int + """The x-coordinate.""" + + y: int + """The y-coordinate.""" + + +class Drag(BaseModel): + """A drag action.""" + + path: List[DragPath] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Literal["drag"] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + +class Keypress(BaseModel): + """A collection of keypresses the model would like to perform.""" + + keys: List[str] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Literal["keypress"] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(BaseModel): + """A mouse move action.""" + + type: Literal["move"] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: int + """The x-coordinate to move to.""" + + y: int + """The y-coordinate to move to.""" + + +class Screenshot(BaseModel): + """A screenshot action.""" + + type: Literal["screenshot"] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(BaseModel): + """A scroll action.""" + + scroll_x: int + """The horizontal scroll distance.""" + + scroll_y: int + """The vertical scroll distance.""" + + type: Literal["scroll"] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: int + """The x-coordinate where the scroll occurred.""" + + y: int + """The y-coordinate where the scroll occurred.""" + + +class Type(BaseModel): + """An action to type in text.""" + + text: str + """The text to type.""" + + type: Literal["type"] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(BaseModel): + """A wait action.""" + + type: Literal["wait"] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +ComputerAction: TypeAlias = Annotated[ + Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/responses/computer_action_list.py b/src/openai/types/responses/computer_action_list.py new file mode 100644 index 0000000000..0198c6e866 --- /dev/null +++ b/src/openai/types/responses/computer_action_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .computer_action import ComputerAction + +__all__ = ["ComputerActionList"] + +ComputerActionList: TypeAlias = List[ComputerAction] diff --git a/src/openai/types/responses/computer_action_list_param.py b/src/openai/types/responses/computer_action_list_param.py new file mode 100644 index 0000000000..ec609ffd1b --- /dev/null +++ b/src/openai/types/responses/computer_action_list_param.py @@ -0,0 +1,183 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "ComputerActionListParam", + "ComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +ComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] + +ComputerActionListParam: TypeAlias = List[ComputerActionParam] diff --git a/src/openai/types/responses/computer_action_param.py b/src/openai/types/responses/computer_action_param.py new file mode 100644 index 0000000000..822a77d759 --- /dev/null +++ b/src/openai/types/responses/computer_action_param.py @@ -0,0 +1,180 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "ComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +ComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] diff --git a/src/openai/types/responses/computer_use_tool.py b/src/openai/types/responses/computer_use_tool.py new file mode 100644 index 0000000000..1704b25424 --- /dev/null +++ b/src/openai/types/responses/computer_use_tool.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ComputerUseTool"] + + +class ComputerUseTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + type: Literal["computer"] + """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/responses/computer_use_tool_param.py b/src/openai/types/responses/computer_use_tool_param.py new file mode 100644 index 0000000000..e81dbe8206 --- /dev/null +++ b/src/openai/types/responses/computer_use_tool_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ComputerUseToolParam"] + + +class ComputerUseToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + type: Required[Literal["computer"]] + """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/responses/custom_tool.py b/src/openai/types/responses/custom_tool.py index 1ca401a486..017c6d699b 100644 --- a/src/openai/types/responses/custom_tool.py +++ b/src/openai/types/responses/custom_tool.py @@ -21,6 +21,9 @@ class CustomTool(BaseModel): type: Literal["custom"] """The type of the custom tool. Always `custom`.""" + defer_loading: Optional[bool] = None + """Whether this tool should be deferred and discovered via tool search.""" + description: Optional[str] = None """Optional description of the custom tool, used to provide more context.""" diff --git a/src/openai/types/responses/custom_tool_param.py b/src/openai/types/responses/custom_tool_param.py index 4ce43cdfdb..e64001366e 100644 --- a/src/openai/types/responses/custom_tool_param.py +++ b/src/openai/types/responses/custom_tool_param.py @@ -21,6 +21,9 @@ class CustomToolParam(TypedDict, total=False): type: Required[Literal["custom"]] """The type of the custom tool. Always `custom`.""" + defer_loading: bool + """Whether this tool should be deferred and discovered via tool search.""" + description: str """Optional description of the custom tool, used to provide more context.""" diff --git a/src/openai/types/responses/easy_input_message.py b/src/openai/types/responses/easy_input_message.py index 6f4d782734..aa97827bc0 100644 --- a/src/openai/types/responses/easy_input_message.py +++ b/src/openai/types/responses/easy_input_message.py @@ -31,12 +31,11 @@ class EasyInputMessage(BaseModel): """ phase: Optional[Literal["commentary", "final_answer"]] = None - """The phase of an assistant message. - - Use `commentary` for an intermediate assistant message and `final_answer` for - the final assistant message. For follow-up requests with models like - `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. - Omitting it can degrade performance. Not used for user messages. + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. """ type: Optional[Literal["message"]] = None diff --git a/src/openai/types/responses/easy_input_message_param.py b/src/openai/types/responses/easy_input_message_param.py index f7eb42ba71..bfc8d577ba 100644 --- a/src/openai/types/responses/easy_input_message_param.py +++ b/src/openai/types/responses/easy_input_message_param.py @@ -32,12 +32,11 @@ class EasyInputMessageParam(TypedDict, total=False): """ phase: Optional[Literal["commentary", "final_answer"]] - """The phase of an assistant message. - - Use `commentary` for an intermediate assistant message and `final_answer` for - the final assistant message. For follow-up requests with models like - `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. - Omitting it can degrade performance. Not used for user messages. + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. """ type: Literal["message"] diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py index b0827a9fa7..6e9751ad88 100644 --- a/src/openai/types/responses/function_tool.py +++ b/src/openai/types/responses/function_tool.py @@ -26,6 +26,9 @@ class FunctionTool(BaseModel): type: Literal["function"] """The type of the function tool. Always `function`.""" + defer_loading: Optional[bool] = None + """Whether this function is deferred and loaded via tool search.""" + description: Optional[str] = None """A description of the function. diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py index ba0a3168c4..e7978b44a2 100644 --- a/src/openai/types/responses/function_tool_param.py +++ b/src/openai/types/responses/function_tool_param.py @@ -26,6 +26,9 @@ class FunctionToolParam(TypedDict, total=False): type: Required[Literal["function"]] """The type of the function tool. Always `function`.""" + defer_loading: bool + """Whether this function is deferred and loaded via tool search.""" + description: Optional[str] """A description of the function. diff --git a/src/openai/types/responses/namespace_tool.py b/src/openai/types/responses/namespace_tool.py new file mode 100644 index 0000000000..2c311dbe17 --- /dev/null +++ b/src/openai/types/responses/namespace_tool.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .custom_tool import CustomTool + +__all__ = ["NamespaceTool", "Tool", "ToolFunction"] + + +class ToolFunction(BaseModel): + name: str + + type: Literal["function"] + + description: Optional[str] = None + + parameters: Optional[object] = None + + strict: Optional[bool] = None + + +Tool: TypeAlias = Annotated[Union[ToolFunction, CustomTool], PropertyInfo(discriminator="type")] + + +class NamespaceTool(BaseModel): + """Groups function/custom tools under a shared namespace.""" + + description: str + """A description of the namespace shown to the model.""" + + name: str + """The namespace name used in tool calls (for example, `crm`).""" + + tools: List[Tool] + """The function/custom tools available inside this namespace.""" + + type: Literal["namespace"] + """The type of the tool. Always `namespace`.""" diff --git a/src/openai/types/responses/namespace_tool_param.py b/src/openai/types/responses/namespace_tool_param.py new file mode 100644 index 0000000000..4bda2ecd83 --- /dev/null +++ b/src/openai/types/responses/namespace_tool_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .custom_tool_param import CustomToolParam + +__all__ = ["NamespaceToolParam", "Tool", "ToolFunction"] + + +class ToolFunction(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["function"]] + + description: Optional[str] + + parameters: Optional[object] + + strict: Optional[bool] + + +Tool: TypeAlias = Union[ToolFunction, CustomToolParam] + + +class NamespaceToolParam(TypedDict, total=False): + """Groups function/custom tools under a shared namespace.""" + + description: Required[str] + """A description of the namespace shown to the model.""" + + name: Required[str] + """The namespace name used in tool calls (for example, `crm`).""" + + tools: Required[Iterable[Tool]] + """The function/custom tools available inside this namespace.""" + + type: Required[Literal["namespace"]] + """The type of the tool. Always `namespace`.""" diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index a859710590..306e52677d 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -20,11 +20,13 @@ from .response_reasoning_item import ResponseReasoningItem from .response_compaction_item import ResponseCompactionItem from .response_custom_tool_call import ResponseCustomToolCall +from .response_tool_search_call import ResponseToolSearchCall from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch from .response_apply_patch_tool_call import ResponseApplyPatchToolCall from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_tool_search_output_item import ResponseToolSearchOutputItem from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput @@ -70,6 +72,8 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): ResponseFileSearchToolCall, ResponseFunctionWebSearch, ResponseComputerToolCall, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, ResponseReasoningItem, McpCall, McpApprovalRequest, diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index 4fb0e7ddc3..95b5da437d 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -14,6 +14,8 @@ class ResponseCompactParams(TypedDict, total=False): model: Required[ Union[ Literal[ + "gpt-5.4", + "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", "gpt-5.2-chat-latest", diff --git a/src/openai/types/responses/response_computer_tool_call.py b/src/openai/types/responses/response_computer_tool_call.py index 4e1b3cf7fd..f796846560 100644 --- a/src/openai/types/responses/response_computer_tool_call.py +++ b/src/openai/types/responses/response_computer_tool_call.py @@ -5,9 +5,11 @@ from ..._utils import PropertyInfo from ..._models import BaseModel +from .computer_action_list import ComputerActionList __all__ = [ "ResponseComputerToolCall", + "PendingSafetyCheck", "Action", "ActionClick", "ActionDoubleClick", @@ -19,10 +21,22 @@ "ActionScroll", "ActionType", "ActionWait", - "PendingSafetyCheck", ] +class PendingSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + class ActionClick(BaseModel): """A click action.""" @@ -194,19 +208,6 @@ class ActionWait(BaseModel): ] -class PendingSafetyCheck(BaseModel): - """A pending safety check for the computer call.""" - - id: str - """The ID of the pending safety check.""" - - code: Optional[str] = None - """The type of the pending safety check.""" - - message: Optional[str] = None - """Details about the pending safety check.""" - - class ResponseComputerToolCall(BaseModel): """A tool call to a computer use tool. @@ -217,9 +218,6 @@ class ResponseComputerToolCall(BaseModel): id: str """The unique ID of the computer call.""" - action: Action - """A click action.""" - call_id: str """An identifier used when responding to the tool call with output.""" @@ -235,3 +233,12 @@ class ResponseComputerToolCall(BaseModel): type: Literal["computer_call"] """The type of the computer call. Always `computer_call`.""" + + action: Optional[Action] = None + """A click action.""" + + actions: Optional[ComputerActionList] = None + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py index 550ba599cd..05cc2c2f67 100644 --- a/src/openai/types/responses/response_computer_tool_call_param.py +++ b/src/openai/types/responses/response_computer_tool_call_param.py @@ -6,9 +6,11 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .computer_action_list_param import ComputerActionListParam __all__ = [ "ResponseComputerToolCallParam", + "PendingSafetyCheck", "Action", "ActionClick", "ActionDoubleClick", @@ -20,10 +22,22 @@ "ActionScroll", "ActionType", "ActionWait", - "PendingSafetyCheck", ] +class PendingSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + class ActionClick(TypedDict, total=False): """A click action.""" @@ -192,19 +206,6 @@ class ActionWait(TypedDict, total=False): ] -class PendingSafetyCheck(TypedDict, total=False): - """A pending safety check for the computer call.""" - - id: Required[str] - """The ID of the pending safety check.""" - - code: Optional[str] - """The type of the pending safety check.""" - - message: Optional[str] - """Details about the pending safety check.""" - - class ResponseComputerToolCallParam(TypedDict, total=False): """A tool call to a computer use tool. @@ -215,9 +216,6 @@ class ResponseComputerToolCallParam(TypedDict, total=False): id: Required[str] """The unique ID of the computer call.""" - action: Required[Action] - """A click action.""" - call_id: Required[str] """An identifier used when responding to the tool call with output.""" @@ -233,3 +231,12 @@ class ResponseComputerToolCallParam(TypedDict, total=False): type: Required[Literal["computer_call"]] """The type of the computer call. Always `computer_call`.""" + + action: Action + """A click action.""" + + actions: ComputerActionListParam + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ diff --git a/src/openai/types/responses/response_custom_tool_call.py b/src/openai/types/responses/response_custom_tool_call.py index f05743966e..965ed88f96 100644 --- a/src/openai/types/responses/response_custom_tool_call.py +++ b/src/openai/types/responses/response_custom_tool_call.py @@ -25,3 +25,6 @@ class ResponseCustomToolCall(BaseModel): id: Optional[str] = None """The unique ID of the custom tool call in the OpenAI platform.""" + + namespace: Optional[str] = None + """The namespace of the custom tool being called.""" diff --git a/src/openai/types/responses/response_custom_tool_call_param.py b/src/openai/types/responses/response_custom_tool_call_param.py index 5d4ce3376c..9f82546ef1 100644 --- a/src/openai/types/responses/response_custom_tool_call_param.py +++ b/src/openai/types/responses/response_custom_tool_call_param.py @@ -24,3 +24,6 @@ class ResponseCustomToolCallParam(TypedDict, total=False): id: str """The unique ID of the custom tool call in the OpenAI platform.""" + + namespace: str + """The namespace of the custom tool being called.""" diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py index 194e3f7d6a..3ff4c67a3f 100644 --- a/src/openai/types/responses/response_function_tool_call.py +++ b/src/openai/types/responses/response_function_tool_call.py @@ -30,6 +30,9 @@ class ResponseFunctionToolCall(BaseModel): id: Optional[str] = None """The unique ID of the function tool call.""" + namespace: Optional[str] = None + """The namespace of the function to run.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None """The status of the item. diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py index 4e8dd3d629..5183e9e233 100644 --- a/src/openai/types/responses/response_function_tool_call_param.py +++ b/src/openai/types/responses/response_function_tool_call_param.py @@ -29,6 +29,9 @@ class ResponseFunctionToolCallParam(TypedDict, total=False): id: str """The unique ID of the function tool call.""" + namespace: str + """The namespace of the function to run.""" + status: Literal["in_progress", "completed", "incomplete"] """The status of the item. diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py index 3e5fb70c5f..97cc176f35 100644 --- a/src/openai/types/responses/response_input_file.py +++ b/src/openai/types/responses/response_input_file.py @@ -14,6 +14,12 @@ class ResponseInputFile(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" + detail: Optional[Literal["low", "high"]] = None + """The detail level of the file to be sent to the model. + + One of `high` or `low`. Defaults to `high`. + """ + file_data: Optional[str] = None """The content of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_content.py b/src/openai/types/responses/response_input_file_content.py index f0dfef55d0..7b1c76bff7 100644 --- a/src/openai/types/responses/response_input_file_content.py +++ b/src/openai/types/responses/response_input_file_content.py @@ -14,6 +14,12 @@ class ResponseInputFileContent(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" + detail: Optional[Literal["high", "low"]] = None + """The detail level of the file to be sent to the model. + + One of `high` or `low`. Defaults to `high`. + """ + file_data: Optional[str] = None """The base64-encoded data of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_content_param.py b/src/openai/types/responses/response_input_file_content_param.py index 376f6c7a45..73e8acd27b 100644 --- a/src/openai/types/responses/response_input_file_content_param.py +++ b/src/openai/types/responses/response_input_file_content_param.py @@ -14,6 +14,12 @@ class ResponseInputFileContentParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" + detail: Literal["high", "low"] + """The detail level of the file to be sent to the model. + + One of `high` or `low`. Defaults to `high`. + """ + file_data: Optional[str] """The base64-encoded data of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py index 8b5da20245..25eec2fe01 100644 --- a/src/openai/types/responses/response_input_file_param.py +++ b/src/openai/types/responses/response_input_file_param.py @@ -14,6 +14,12 @@ class ResponseInputFileParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" + detail: Literal["low", "high"] + """The detail level of the file to be sent to the model. + + One of `high` or `low`. Defaults to `high`. + """ + file_data: str """The content of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py index 500bc4b346..63c4b42cbd 100644 --- a/src/openai/types/responses/response_input_image.py +++ b/src/openai/types/responses/response_input_image.py @@ -14,10 +14,10 @@ class ResponseInputImage(BaseModel): Learn about [image inputs](https://platform.openai.com/docs/guides/vision). """ - detail: Literal["low", "high", "auto"] + detail: Literal["low", "high", "auto", "original"] """The detail level of the image to be sent to the model. - One of `high`, `low`, or `auto`. Defaults to `auto`. + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. """ type: Literal["input_image"] diff --git a/src/openai/types/responses/response_input_image_content.py b/src/openai/types/responses/response_input_image_content.py index e38bc28d5e..d9619f65d8 100644 --- a/src/openai/types/responses/response_input_image_content.py +++ b/src/openai/types/responses/response_input_image_content.py @@ -17,10 +17,10 @@ class ResponseInputImageContent(BaseModel): type: Literal["input_image"] """The type of the input item. Always `input_image`.""" - detail: Optional[Literal["low", "high", "auto"]] = None + detail: Optional[Literal["low", "high", "auto", "original"]] = None """The detail level of the image to be sent to the model. - One of `high`, `low`, or `auto`. Defaults to `auto`. + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. """ file_id: Optional[str] = None diff --git a/src/openai/types/responses/response_input_image_content_param.py b/src/openai/types/responses/response_input_image_content_param.py index c21f46d736..743642d4cf 100644 --- a/src/openai/types/responses/response_input_image_content_param.py +++ b/src/openai/types/responses/response_input_image_content_param.py @@ -17,10 +17,10 @@ class ResponseInputImageContentParam(TypedDict, total=False): type: Required[Literal["input_image"]] """The type of the input item. Always `input_image`.""" - detail: Optional[Literal["low", "high", "auto"]] + detail: Optional[Literal["low", "high", "auto", "original"]] """The detail level of the image to be sent to the model. - One of `high`, `low`, or `auto`. Defaults to `auto`. + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. """ file_id: Optional[str] diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py index fd8c1bd070..118f0b5f72 100644 --- a/src/openai/types/responses/response_input_image_param.py +++ b/src/openai/types/responses/response_input_image_param.py @@ -14,10 +14,10 @@ class ResponseInputImageParam(TypedDict, total=False): Learn about [image inputs](https://platform.openai.com/docs/guides/vision). """ - detail: Required[Literal["low", "high", "auto"]] + detail: Required[Literal["low", "high", "auto", "original"]] """The detail level of the image to be sent to the model. - One of `high`, `low`, or `auto`. Defaults to `auto`. + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. """ type: Required[Literal["input_image"]] diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index 2b664fd875..af3ce5bdc9 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -19,6 +19,7 @@ from .response_custom_tool_call_output import ResponseCustomToolCallOutput from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_input_message_content_list import ResponseInputMessageContentList +from .response_tool_search_output_item_param import ResponseToolSearchOutputItemParam from .response_function_call_output_item_list import ResponseFunctionCallOutputItemList from .response_function_shell_call_output_content import ResponseFunctionShellCallOutputContent from .response_computer_tool_call_output_screenshot import ResponseComputerToolCallOutputScreenshot @@ -29,6 +30,7 @@ "ComputerCallOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", + "ToolSearchCall", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -147,6 +149,26 @@ class FunctionCallOutput(BaseModel): """ +class ToolSearchCall(BaseModel): + arguments: object + """The arguments supplied to the tool search call.""" + + type: Literal["tool_search_call"] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] = None + """The unique ID of this tool search call.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search call.""" + + class ImageGenerationCall(BaseModel): """An image generation request made by the model.""" @@ -526,6 +548,8 @@ class ItemReference(BaseModel): ResponseFunctionWebSearch, ResponseFunctionToolCall, FunctionCallOutput, + ToolSearchCall, + ResponseToolSearchOutputItemParam, ResponseReasoningItem, ResponseCompactionItemParam, ImageGenerationCall, diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index f52a7495db..87ea1bc572 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -20,6 +20,7 @@ from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_tool_search_output_item_param_param import ResponseToolSearchOutputItemParamParam from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam @@ -30,6 +31,7 @@ "ComputerCallOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", + "ToolSearchCall", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -148,6 +150,26 @@ class FunctionCallOutput(TypedDict, total=False): """ +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + class ImageGenerationCall(TypedDict, total=False): """An image generation request made by the model.""" @@ -523,6 +545,8 @@ class ItemReference(TypedDict, total=False): ResponseFunctionWebSearchParam, ResponseFunctionToolCallParam, FunctionCallOutput, + ToolSearchCall, + ResponseToolSearchOutputItemParamParam, ResponseReasoningItemParam, ResponseCompactionItemParamParam, ImageGenerationCall, diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index c125d03f84..cf4d529521 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -20,6 +20,7 @@ from .response_custom_tool_call_output_param import ResponseCustomToolCallOutputParam from .response_code_interpreter_tool_call_param import ResponseCodeInterpreterToolCallParam from .response_input_message_content_list_param import ResponseInputMessageContentListParam +from .response_tool_search_output_item_param_param import ResponseToolSearchOutputItemParamParam from .response_function_call_output_item_list_param import ResponseFunctionCallOutputItemListParam from .response_function_shell_call_output_content_param import ResponseFunctionShellCallOutputContentParam from .response_computer_tool_call_output_screenshot_param import ResponseComputerToolCallOutputScreenshotParam @@ -31,6 +32,7 @@ "ComputerCallOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", + "ToolSearchCall", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -149,6 +151,26 @@ class FunctionCallOutput(TypedDict, total=False): """ +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + class ImageGenerationCall(TypedDict, total=False): """An image generation request made by the model.""" @@ -524,6 +546,8 @@ class ItemReference(TypedDict, total=False): ResponseFunctionWebSearchParam, ResponseFunctionToolCallParam, FunctionCallOutput, + ToolSearchCall, + ResponseToolSearchOutputItemParamParam, ResponseReasoningItemParam, ResponseCompactionItemParamParam, ImageGenerationCall, diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py index 3dba681d53..316a912f62 100644 --- a/src/openai/types/responses/response_item.py +++ b/src/openai/types/responses/response_item.py @@ -6,12 +6,14 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from .response_output_message import ResponseOutputMessage +from .response_tool_search_call import ResponseToolSearchCall from .response_computer_tool_call import ResponseComputerToolCall from .response_input_message_item import ResponseInputMessageItem from .response_function_web_search import ResponseFunctionWebSearch from .response_apply_patch_tool_call import ResponseApplyPatchToolCall from .response_file_search_tool_call import ResponseFileSearchToolCall from .response_function_tool_call_item import ResponseFunctionToolCallItem +from .response_tool_search_output_item import ResponseToolSearchOutputItem from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput @@ -227,6 +229,8 @@ class McpCall(BaseModel): ResponseFunctionWebSearch, ResponseFunctionToolCallItem, ResponseFunctionToolCallOutputItem, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, ImageGenerationCall, ResponseCodeInterpreterToolCall, LocalShellCall, diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index 990f947b90..4be4cbf78e 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -9,11 +9,13 @@ from .response_reasoning_item import ResponseReasoningItem from .response_compaction_item import ResponseCompactionItem from .response_custom_tool_call import ResponseCustomToolCall +from .response_tool_search_call import ResponseToolSearchCall from .response_computer_tool_call import ResponseComputerToolCall from .response_function_tool_call import ResponseFunctionToolCall from .response_function_web_search import ResponseFunctionWebSearch from .response_apply_patch_tool_call import ResponseApplyPatchToolCall from .response_file_search_tool_call import ResponseFileSearchToolCall +from .response_tool_search_output_item import ResponseToolSearchOutputItem from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput @@ -188,6 +190,8 @@ class McpApprovalRequest(BaseModel): ResponseFunctionWebSearch, ResponseComputerToolCall, ResponseReasoningItem, + ResponseToolSearchCall, + ResponseToolSearchOutputItem, ResponseCompactionItem, ImageGenerationCall, ResponseCodeInterpreterToolCall, diff --git a/src/openai/types/responses/response_output_message.py b/src/openai/types/responses/response_output_message.py index a8720e1c57..760d72d58d 100644 --- a/src/openai/types/responses/response_output_message.py +++ b/src/openai/types/responses/response_output_message.py @@ -36,10 +36,9 @@ class ResponseOutputMessage(BaseModel): """The type of the output message. Always `message`.""" phase: Optional[Literal["commentary", "final_answer"]] = None - """The phase of an assistant message. - - Use `commentary` for an intermediate assistant message and `final_answer` for - the final assistant message. For follow-up requests with models like - `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. - Omitting it can degrade performance. Not used for user messages. + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. """ diff --git a/src/openai/types/responses/response_output_message_param.py b/src/openai/types/responses/response_output_message_param.py index 5d488d8c6b..09fec5bd58 100644 --- a/src/openai/types/responses/response_output_message_param.py +++ b/src/openai/types/responses/response_output_message_param.py @@ -36,10 +36,9 @@ class ResponseOutputMessageParam(TypedDict, total=False): """The type of the output message. Always `message`.""" phase: Optional[Literal["commentary", "final_answer"]] - """The phase of an assistant message. - - Use `commentary` for an intermediate assistant message and `final_answer` for - the final assistant message. For follow-up requests with models like - `gpt-5.3-codex` and later, preserve and resend phase on all assistant messages. - Omitting it can degrade performance. Not used for user messages. + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. """ diff --git a/src/openai/types/responses/response_tool_search_call.py b/src/openai/types/responses/response_tool_search_call.py new file mode 100644 index 0000000000..495bf17119 --- /dev/null +++ b/src/openai/types/responses/response_tool_search_call.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ResponseToolSearchCall"] + + +class ResponseToolSearchCall(BaseModel): + id: str + """The unique ID of the tool search call item.""" + + arguments: object + """Arguments used for the tool search call.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search call item that was recorded.""" + + type: Literal["tool_search_call"] + """The type of the item. Always `tool_search_call`.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_tool_search_output_item.py b/src/openai/types/responses/response_tool_search_output_item.py new file mode 100644 index 0000000000..f8911dd491 --- /dev/null +++ b/src/openai/types/responses/response_tool_search_output_item.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .tool import Tool +from ..._models import BaseModel + +__all__ = ["ResponseToolSearchOutputItem"] + + +class ResponseToolSearchOutputItem(BaseModel): + id: str + """The unique ID of the tool search output item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search output item that was recorded.""" + + tools: List[Tool] + """The loaded tool definitions returned by tool search.""" + + type: Literal["tool_search_output"] + """The type of the item. Always `tool_search_output`.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_tool_search_output_item_param.py b/src/openai/types/responses/response_tool_search_output_item_param.py new file mode 100644 index 0000000000..f4d4ef34c6 --- /dev/null +++ b/src/openai/types/responses/response_tool_search_output_item_param.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from .tool import Tool +from ..._models import BaseModel + +__all__ = ["ResponseToolSearchOutputItemParam"] + + +class ResponseToolSearchOutputItemParam(BaseModel): + tools: List[Tool] + """The loaded tool definitions returned by the tool search output.""" + + type: Literal["tool_search_output"] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] = None + """The unique ID of this tool search output.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search output.""" diff --git a/src/openai/types/responses/response_tool_search_output_item_param_param.py b/src/openai/types/responses/response_tool_search_output_item_param_param.py new file mode 100644 index 0000000000..28e9b1e186 --- /dev/null +++ b/src/openai/types/responses/response_tool_search_output_item_param_param.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .tool_param import ToolParam + +__all__ = ["ResponseToolSearchOutputItemParamParam"] + + +class ResponseToolSearchOutputItemParamParam(TypedDict, total=False): + tools: Required[Iterable[ToolParam]] + """The loaded tool definitions returned by the tool search output.""" + + type: Required[Literal["tool_search_output"]] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] + """The unique ID of this tool search output.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search output.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index a98b4ca758..140cd520c7 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -9,9 +9,12 @@ from .custom_tool import CustomTool from .computer_tool import ComputerTool from .function_tool import FunctionTool +from .namespace_tool import NamespaceTool from .web_search_tool import WebSearchTool from .apply_patch_tool import ApplyPatchTool from .file_search_tool import FileSearchTool +from .tool_search_tool import ToolSearchTool +from .computer_use_tool import ComputerUseTool from .function_shell_tool import FunctionShellTool from .web_search_preview_tool import WebSearchPreviewTool from .container_network_policy_disabled import ContainerNetworkPolicyDisabled @@ -158,6 +161,9 @@ class Mcp(BaseModel): - SharePoint: `connector_sharepoint` """ + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] = None """Optional HTTP headers to send to the MCP server. @@ -306,6 +312,7 @@ class LocalShell(BaseModel): Union[ FunctionTool, FileSearchTool, + ComputerUseTool, ComputerTool, WebSearchTool, Mcp, @@ -314,6 +321,8 @@ class LocalShell(BaseModel): LocalShell, FunctionShellTool, CustomTool, + NamespaceTool, + ToolSearchTool, WebSearchPreviewTool, ApplyPatchTool, ], diff --git a/src/openai/types/responses/tool_choice_types.py b/src/openai/types/responses/tool_choice_types.py index 044c014b19..3cd1d3f64c 100644 --- a/src/openai/types/responses/tool_choice_types.py +++ b/src/openai/types/responses/tool_choice_types.py @@ -16,7 +16,9 @@ class ToolChoiceTypes(BaseModel): type: Literal[ "file_search", "web_search_preview", + "computer", "computer_use_preview", + "computer_use", "web_search_preview_2025_03_11", "image_generation", "code_interpreter", @@ -30,7 +32,9 @@ class ToolChoiceTypes(BaseModel): - `file_search` - `web_search_preview` + - `computer` - `computer_use_preview` + - `computer_use` - `code_interpreter` - `image_generation` """ diff --git a/src/openai/types/responses/tool_choice_types_param.py b/src/openai/types/responses/tool_choice_types_param.py index 9bf02dbfcc..5d08380a22 100644 --- a/src/openai/types/responses/tool_choice_types_param.py +++ b/src/openai/types/responses/tool_choice_types_param.py @@ -17,7 +17,9 @@ class ToolChoiceTypesParam(TypedDict, total=False): Literal[ "file_search", "web_search_preview", + "computer", "computer_use_preview", + "computer_use", "web_search_preview_2025_03_11", "image_generation", "code_interpreter", @@ -32,7 +34,9 @@ class ToolChoiceTypesParam(TypedDict, total=False): - `file_search` - `web_search_preview` + - `computer` - `computer_use_preview` + - `computer_use` - `code_interpreter` - `image_generation` """ diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index a351065dea..e35d178d6e 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -11,9 +11,12 @@ from .custom_tool_param import CustomToolParam from .computer_tool_param import ComputerToolParam from .function_tool_param import FunctionToolParam +from .namespace_tool_param import NamespaceToolParam from .web_search_tool_param import WebSearchToolParam from .apply_patch_tool_param import ApplyPatchToolParam from .file_search_tool_param import FileSearchToolParam +from .tool_search_tool_param import ToolSearchToolParam +from .computer_use_tool_param import ComputerUseToolParam from .function_shell_tool_param import FunctionShellToolParam from .web_search_preview_tool_param import WebSearchPreviewToolParam from .container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam @@ -158,6 +161,9 @@ class Mcp(TypedDict, total=False): - SharePoint: `connector_sharepoint` """ + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + headers: Optional[Dict[str, str]] """Optional HTTP headers to send to the MCP server. @@ -305,6 +311,7 @@ class LocalShell(TypedDict, total=False): ToolParam: TypeAlias = Union[ FunctionToolParam, FileSearchToolParam, + ComputerUseToolParam, ComputerToolParam, WebSearchToolParam, Mcp, @@ -313,6 +320,8 @@ class LocalShell(TypedDict, total=False): LocalShell, FunctionShellToolParam, CustomToolParam, + NamespaceToolParam, + ToolSearchToolParam, WebSearchPreviewToolParam, ApplyPatchToolParam, ] diff --git a/src/openai/types/responses/tool_search_tool.py b/src/openai/types/responses/tool_search_tool.py new file mode 100644 index 0000000000..44a741a1c4 --- /dev/null +++ b/src/openai/types/responses/tool_search_tool.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ToolSearchTool"] + + +class ToolSearchTool(BaseModel): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Literal["tool_search"] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] = None + """Description shown to the model for a client-executed tool search tool.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] = None + """Parameter schema for a client-executed tool search tool.""" diff --git a/src/openai/types/responses/tool_search_tool_param.py b/src/openai/types/responses/tool_search_tool_param.py new file mode 100644 index 0000000000..3063da276c --- /dev/null +++ b/src/openai/types/responses/tool_search_tool_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ToolSearchToolParam"] + + +class ToolSearchToolParam(TypedDict, total=False): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Required[Literal["tool_search"]] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] + """Description shown to the model for a client-executed tool search tool.""" + + execution: Literal["server", "client"] + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] + """Parameter schema for a client-executed tool search tool.""" diff --git a/src/openai/types/responses/web_search_preview_tool.py b/src/openai/types/responses/web_search_preview_tool.py index 12478e896d..bdf092f196 100644 --- a/src/openai/types/responses/web_search_preview_tool.py +++ b/src/openai/types/responses/web_search_preview_tool.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel @@ -45,6 +45,8 @@ class WebSearchPreviewTool(BaseModel): One of `web_search_preview` or `web_search_preview_2025_03_11`. """ + search_content_types: Optional[List[Literal["text", "image"]]] = None + search_context_size: Optional[Literal["low", "medium", "high"]] = None """High level guidance for the amount of context window space to use for the search. diff --git a/src/openai/types/responses/web_search_preview_tool_param.py b/src/openai/types/responses/web_search_preview_tool_param.py index 09619a3394..b81f95e308 100644 --- a/src/openai/types/responses/web_search_preview_tool_param.py +++ b/src/openai/types/responses/web_search_preview_tool_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import List, Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["WebSearchPreviewToolParam", "UserLocation"] @@ -45,6 +45,8 @@ class WebSearchPreviewToolParam(TypedDict, total=False): One of `web_search_preview` or `web_search_preview_2025_03_11`. """ + search_content_types: List[Literal["text", "image"]] + search_context_size: Literal["low", "medium", "high"] """High level guidance for the amount of context window space to use for the search. diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index 8223b81bef..b86bd28ef9 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -5,6 +5,8 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.4", + "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", "gpt-5.2-chat-latest", diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index c1937a8312..2a45c8f423 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -7,6 +7,8 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.4", + "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", "gpt-5.2-chat-latest", diff --git a/src/openai/types/video.py b/src/openai/types/video.py index e732ea54ec..051b951ede 100644 --- a/src/openai/types/video.py +++ b/src/openai/types/video.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Union, Optional from typing_extensions import Literal from .._models import BaseModel @@ -45,8 +45,11 @@ class Video(BaseModel): remixed_from_video_id: Optional[str] = None """Identifier of the source video if this video is a remix.""" - seconds: VideoSeconds - """Duration of the generated clip in seconds.""" + seconds: Union[str, VideoSeconds] + """Duration of the generated clip in seconds. + + For extensions, this is the stitched total duration. + """ size: VideoSize """The resolution of the generated video.""" diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py index d787aaeddd..282b1db429 100644 --- a/src/openai/types/video_create_params.py +++ b/src/openai/types/video_create_params.py @@ -17,7 +17,7 @@ class VideoCreateParams(TypedDict, total=False): """Text prompt that describes the video to generate.""" input_reference: FileTypes - """Optional image reference that guides generation.""" + """Optional multipart reference asset that guides generation.""" model: VideoModelParam """The video generation model to use (allowed values: sora-2, sora-2-pro). diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 2b58ff8191..995b752e11 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -31,7 +31,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -45,7 +45,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "name": "name", } ], - model="gpt-4o", + model="gpt-5.4", audio={ "format": "wav", "voice": "ash", @@ -127,7 +127,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) assert response.is_closed is True @@ -144,7 +144,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -163,7 +163,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, ) completion_stream.response.close() @@ -178,7 +178,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "name": "name", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, audio={ "format": "wav", @@ -260,7 +260,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, ) @@ -277,7 +277,7 @@ def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, ) as response: assert not response.is_closed @@ -474,7 +474,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) assert_matches_type(ChatCompletion, completion, path=["response"]) @@ -488,7 +488,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "name": "name", } ], - model="gpt-4o", + model="gpt-5.4", audio={ "format": "wav", "voice": "ash", @@ -570,7 +570,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) assert response.is_closed is True @@ -587,7 +587,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -606,7 +606,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, ) await completion_stream.response.aclose() @@ -621,7 +621,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "name": "name", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, audio={ "format": "wav", @@ -703,7 +703,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, ) @@ -720,7 +720,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncOpe "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", stream=True, ) as response: assert not response.is_closed diff --git a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py index 9420e3a34c..a3118fc838 100644 --- a/tests/api_resources/fine_tuning/checkpoints/test_permissions.py +++ b/tests/api_resources/fine_tuning/checkpoints/test_permissions.py @@ -9,13 +9,16 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncPage, AsyncPage +from openai.pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage from openai.types.fine_tuning.checkpoints import ( + PermissionListResponse, PermissionCreateResponse, PermissionDeleteResponse, PermissionRetrieveResponse, ) +# pyright: reportDeprecated=false + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -68,52 +71,110 @@ def test_path_params_create(self, client: OpenAI) -> None: @parametrize def test_method_retrieve(self, client: OpenAI) -> None: - permission = client.fine_tuning.checkpoints.permissions.retrieve( - fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) + with pytest.warns(DeprecationWarning): + permission = client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: - permission = client.fine_tuning.checkpoints.permissions.retrieve( + with pytest.warns(DeprecationWarning): + permission = client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="after", + limit=0, + order="ascending", + project_id="project_id", + ) + + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + with client.fine_tuning.checkpoints.permissions.with_streaming_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.warns(DeprecationWarning): + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + permission = client.fine_tuning.checkpoints.permissions.list( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(SyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + permission = client.fine_tuning.checkpoints.permissions.list( fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", after="after", limit=0, order="ascending", project_id="project_id", ) - assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + assert_matches_type(SyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) @parametrize - def test_raw_response_retrieve(self, client: OpenAI) -> None: - response = client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.fine_tuning.checkpoints.permissions.with_raw_response.list( fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" permission = response.parse() - assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + assert_matches_type(SyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) @parametrize - def test_streaming_response_retrieve(self, client: OpenAI) -> None: - with client.fine_tuning.checkpoints.permissions.with_streaming_response.retrieve( + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.fine_tuning.checkpoints.permissions.with_streaming_response.list( fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" permission = response.parse() - assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + assert_matches_type(SyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_retrieve(self, client: OpenAI) -> None: + def test_path_params_list(self, client: OpenAI) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" ): - client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + client.fine_tuning.checkpoints.permissions.with_raw_response.list( fine_tuned_model_checkpoint="", ) @@ -219,52 +280,110 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: - permission = await async_client.fine_tuning.checkpoints.permissions.retrieve( - fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", - ) + with pytest.warns(DeprecationWarning): + permission = await async_client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: - permission = await async_client.fine_tuning.checkpoints.permissions.retrieve( + with pytest.warns(DeprecationWarning): + permission = await async_client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + after="after", + limit=0, + order="ascending", + project_id="project_id", + ) + + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.retrieve( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = await response.parse() + assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.warns(DeprecationWarning): + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" + ): + await async_client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + fine_tuned_model_checkpoint="", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + permission = await async_client.fine_tuning.checkpoints.permissions.list( + fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", + ) + assert_matches_type(AsyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + permission = await async_client.fine_tuning.checkpoints.permissions.list( fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", after="after", limit=0, order="ascending", project_id="project_id", ) - assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: - response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.fine_tuning.checkpoints.permissions.with_raw_response.list( fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" permission = response.parse() - assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: - async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.retrieve( + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.fine_tuning.checkpoints.permissions.with_streaming_response.list( fine_tuned_model_checkpoint="ft-AF1WoRqd3aJAHsqc9NY7iL8F", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" permission = await response.parse() - assert_matches_type(PermissionRetrieveResponse, permission, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[PermissionListResponse], permission, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: with pytest.raises( ValueError, match=r"Expected a non-empty value for `fine_tuned_model_checkpoint` but received ''" ): - await async_client.fine_tuning.checkpoints.permissions.with_raw_response.retrieve( + await async_client.fine_tuning.checkpoints.permissions.with_raw_response.list( fine_tuned_model_checkpoint="", ) diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py index d9aecc33bd..b4bc627837 100644 --- a/tests/api_resources/responses/test_input_tokens.py +++ b/tests/api_resources/responses/test_input_tokens.py @@ -47,6 +47,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "defer_loading": True, "description": "description", } ], @@ -110,6 +111,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "defer_loading": True, "description": "description", } ], diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 8f82f2046d..deaf35970f 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -75,6 +75,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "defer_loading": True, "description": "description", } ], @@ -161,6 +162,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "defer_loading": True, "description": "description", } ], @@ -374,14 +376,14 @@ def test_path_params_cancel(self, client: OpenAI) -> None: @parametrize def test_method_compact(self, client: OpenAI) -> None: response = client.responses.compact( - model="gpt-5.2", + model="gpt-5.4", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize def test_method_compact_with_all_params(self, client: OpenAI) -> None: response = client.responses.compact( - model="gpt-5.2", + model="gpt-5.4", input="string", instructions="instructions", previous_response_id="resp_123", @@ -392,7 +394,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_compact(self, client: OpenAI) -> None: http_response = client.responses.with_raw_response.compact( - model="gpt-5.2", + model="gpt-5.4", ) assert http_response.is_closed is True @@ -403,7 +405,7 @@ def test_raw_response_compact(self, client: OpenAI) -> None: @parametrize def test_streaming_response_compact(self, client: OpenAI) -> None: with client.responses.with_streaming_response.compact( - model="gpt-5.2", + model="gpt-5.4", ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -484,6 +486,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "defer_loading": True, "description": "description", } ], @@ -570,6 +573,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "defer_loading": True, "description": "description", } ], @@ -783,14 +787,14 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_compact(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.compact( - model="gpt-5.2", + model="gpt-5.4", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.compact( - model="gpt-5.2", + model="gpt-5.4", input="string", instructions="instructions", previous_response_id="resp_123", @@ -801,7 +805,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) - @parametrize async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: http_response = await async_client.responses.with_raw_response.compact( - model="gpt-5.2", + model="gpt-5.4", ) assert http_response.is_closed is True @@ -812,7 +816,7 @@ async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None: async with async_client.responses.with_streaming_response.compact( - model="gpt-5.2", + model="gpt-5.4", ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/test_client.py b/tests/test_client.py index b8b963aa4c..a015cd7d40 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -877,7 +877,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ).__enter__() assert _get_open_connections(client) == 0 @@ -895,7 +895,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ).__enter__() assert _get_open_connections(client) == 0 @@ -932,7 +932,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) assert response.retries_taken == failures_before_success @@ -964,7 +964,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", extra_headers={"x-stainless-retry-count": Omit()}, ) @@ -996,7 +996,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", extra_headers={"x-stainless-retry-count": "42"}, ) @@ -1028,7 +1028,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) as response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1924,7 +1924,7 @@ async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1942,7 +1942,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ).__aenter__() assert _get_open_connections(async_client) == 0 @@ -1979,7 +1979,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) assert response.retries_taken == failures_before_success @@ -2011,7 +2011,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", extra_headers={"x-stainless-retry-count": Omit()}, ) @@ -2043,7 +2043,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", extra_headers={"x-stainless-retry-count": "42"}, ) @@ -2075,7 +2075,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: "role": "developer", } ], - model="gpt-4o", + model="gpt-5.4", ) as response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success From 15afa21e54952c06e2ac4d3e3a82f144c2cf9ed9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 18:13:25 -0500 Subject: [PATCH 255/408] release: 2.26.0 (#2932) * feat(api): The GA ComputerTool now uses the CompuerTool class. The 'computer_use_preview' tool is moved to ComputerUsePreview This fixes naming of the old computer_use_preview, which previously took the ComputerTool name. There is a newly GAed `computer` tool now available, which will use the ComputerTool name. This may be a breaking change for users of the preview tool. * release: 2.26.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- .stats.yml | 2 +- CHANGELOG.md | 8 ++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/responses/api.md | 2 +- src/openai/types/responses/__init__.py | 4 +-- src/openai/types/responses/computer_tool.py | 13 ++-------- .../types/responses/computer_tool_param.py | 13 ++-------- .../responses/computer_use_preview_tool.py | 26 +++++++++++++++++++ .../computer_use_preview_tool_param.py | 26 +++++++++++++++++++ .../types/responses/computer_use_tool.py | 17 ------------ .../responses/computer_use_tool_param.py | 17 ------------ src/openai/types/responses/tool.py | 4 +-- src/openai/types/responses/tool_param.py | 4 +-- 15 files changed, 75 insertions(+), 67 deletions(-) create mode 100644 src/openai/types/responses/computer_use_preview_tool.py create mode 100644 src/openai/types/responses/computer_use_preview_tool_param.py delete mode 100644 src/openai/types/responses/computer_use_tool.py delete mode 100644 src/openai/types/responses/computer_use_tool_param.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c5fe8ab6d6..1441304df6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.25.0" + ".": "2.26.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index a59953aaa9..bd550123f1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 148 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9c802d45a9bf2a896b5fd22ac22bba185e8a145bd40ed242df9bb87a05e954eb.yml openapi_spec_hash: 97984ed69285e660b7d5c810c69ed449 -config_hash: acb0b1eb5d7284bfedaddb29f7f5a691 +config_hash: 8240b8a7a7fc145a45b93bda435612d6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 780a961a43..c8508a0b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.26.0 (2026-03-05) + +Full Changelog: [v2.25.0...v2.26.0](https://github.com/openai/openai-python/compare/v2.25.0...v2.26.0) + +### Features + +* **api:** The GA ComputerTool now uses the CompuerTool class. The 'computer_use_preview' tool is moved to ComputerUsePreview ([78f5b3c](https://github.com/openai/openai-python/commit/78f5b3c287b71ed6fbeb71fb6b5c0366db704cd2)) + ## 2.25.0 (2026-03-05) Full Changelog: [v2.24.0...v2.25.0](https://github.com/openai/openai-python/compare/v2.24.0...v2.25.0) diff --git a/pyproject.toml b/pyproject.toml index f7aae6cbdb..41738c8c5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.25.0" +version = "2.26.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 417b40c283..2feccec170 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.25.0" # x-release-please-version +__version__ = "2.26.0" # x-release-please-version diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md index d654abb99a..d211cd9899 100644 --- a/src/openai/resources/responses/api.md +++ b/src/openai/resources/responses/api.md @@ -9,7 +9,7 @@ from openai.types.responses import ( ComputerAction, ComputerActionList, ComputerTool, - ComputerUseTool, + ComputerUsePreviewTool, ContainerAuto, ContainerNetworkPolicyAllowlist, ContainerNetworkPolicyDisabled, diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index 714347b05d..d06f17f6a3 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -33,7 +33,6 @@ from .apply_patch_tool import ApplyPatchTool as ApplyPatchTool from .file_search_tool import FileSearchTool as FileSearchTool from .tool_search_tool import ToolSearchTool as ToolSearchTool -from .computer_use_tool import ComputerUseTool as ComputerUseTool from .custom_tool_param import CustomToolParam as CustomToolParam from .local_environment import LocalEnvironment as LocalEnvironment from .local_skill_param import LocalSkillParam as LocalSkillParam @@ -83,7 +82,6 @@ from .responses_client_event import ResponsesClientEvent as ResponsesClientEvent from .responses_server_event import ResponsesServerEvent as ResponsesServerEvent from .tool_search_tool_param import ToolSearchToolParam as ToolSearchToolParam -from .computer_use_tool_param import ComputerUseToolParam as ComputerUseToolParam from .local_environment_param import LocalEnvironmentParam as LocalEnvironmentParam from .response_compact_params import ResponseCompactParams as ResponseCompactParams from .response_output_message import ResponseOutputMessage as ResponseOutputMessage @@ -100,6 +98,7 @@ from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent from .tool_choice_custom_param import ToolChoiceCustomParam as ToolChoiceCustomParam +from .computer_use_preview_tool import ComputerUsePreviewTool as ComputerUsePreviewTool from .container_reference_param import ContainerReferenceParam as ContainerReferenceParam from .function_shell_tool_param import FunctionShellToolParam as FunctionShellToolParam from .inline_skill_source_param import InlineSkillSourceParam as InlineSkillSourceParam @@ -145,6 +144,7 @@ from .response_compaction_item_param import ResponseCompactionItemParam as ResponseCompactionItemParam from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent +from .computer_use_preview_tool_param import ComputerUsePreviewToolParam as ComputerUsePreviewToolParam from .response_custom_tool_call_param import ResponseCustomToolCallParam as ResponseCustomToolCallParam from .response_output_item_done_event import ResponseOutputItemDoneEvent as ResponseOutputItemDoneEvent from .response_content_part_done_event import ResponseContentPartDoneEvent as ResponseContentPartDoneEvent diff --git a/src/openai/types/responses/computer_tool.py b/src/openai/types/responses/computer_tool.py index 22871c841c..392faa9e79 100644 --- a/src/openai/types/responses/computer_tool.py +++ b/src/openai/types/responses/computer_tool.py @@ -13,14 +13,5 @@ class ComputerTool(BaseModel): Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). """ - display_height: int - """The height of the computer display.""" - - display_width: int - """The width of the computer display.""" - - environment: Literal["windows", "mac", "linux", "ubuntu", "browser"] - """The type of computer environment to control.""" - - type: Literal["computer_use_preview"] - """The type of the computer use tool. Always `computer_use_preview`.""" + type: Literal["computer"] + """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/responses/computer_tool_param.py b/src/openai/types/responses/computer_tool_param.py index cdf75a43f2..b5931dea4f 100644 --- a/src/openai/types/responses/computer_tool_param.py +++ b/src/openai/types/responses/computer_tool_param.py @@ -13,14 +13,5 @@ class ComputerToolParam(TypedDict, total=False): Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). """ - display_height: Required[int] - """The height of the computer display.""" - - display_width: Required[int] - """The width of the computer display.""" - - environment: Required[Literal["windows", "mac", "linux", "ubuntu", "browser"]] - """The type of computer environment to control.""" - - type: Required[Literal["computer_use_preview"]] - """The type of the computer use tool. Always `computer_use_preview`.""" + type: Required[Literal["computer"]] + """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/responses/computer_use_preview_tool.py b/src/openai/types/responses/computer_use_preview_tool.py new file mode 100644 index 0000000000..686860e2cf --- /dev/null +++ b/src/openai/types/responses/computer_use_preview_tool.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ComputerUsePreviewTool"] + + +class ComputerUsePreviewTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + display_height: int + """The height of the computer display.""" + + display_width: int + """The width of the computer display.""" + + environment: Literal["windows", "mac", "linux", "ubuntu", "browser"] + """The type of computer environment to control.""" + + type: Literal["computer_use_preview"] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/src/openai/types/responses/computer_use_preview_tool_param.py b/src/openai/types/responses/computer_use_preview_tool_param.py new file mode 100644 index 0000000000..2611c7297f --- /dev/null +++ b/src/openai/types/responses/computer_use_preview_tool_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ComputerUsePreviewToolParam"] + + +class ComputerUsePreviewToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + display_height: Required[int] + """The height of the computer display.""" + + display_width: Required[int] + """The width of the computer display.""" + + environment: Required[Literal["windows", "mac", "linux", "ubuntu", "browser"]] + """The type of computer environment to control.""" + + type: Required[Literal["computer_use_preview"]] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/src/openai/types/responses/computer_use_tool.py b/src/openai/types/responses/computer_use_tool.py deleted file mode 100644 index 1704b25424..0000000000 --- a/src/openai/types/responses/computer_use_tool.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["ComputerUseTool"] - - -class ComputerUseTool(BaseModel): - """A tool that controls a virtual computer. - - Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). - """ - - type: Literal["computer"] - """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/responses/computer_use_tool_param.py b/src/openai/types/responses/computer_use_tool_param.py deleted file mode 100644 index e81dbe8206..0000000000 --- a/src/openai/types/responses/computer_use_tool_param.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ComputerUseToolParam"] - - -class ComputerUseToolParam(TypedDict, total=False): - """A tool that controls a virtual computer. - - Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). - """ - - type: Required[Literal["computer"]] - """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 140cd520c7..34120a287e 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -14,9 +14,9 @@ from .apply_patch_tool import ApplyPatchTool from .file_search_tool import FileSearchTool from .tool_search_tool import ToolSearchTool -from .computer_use_tool import ComputerUseTool from .function_shell_tool import FunctionShellTool from .web_search_preview_tool import WebSearchPreviewTool +from .computer_use_preview_tool import ComputerUsePreviewTool from .container_network_policy_disabled import ContainerNetworkPolicyDisabled from .container_network_policy_allowlist import ContainerNetworkPolicyAllowlist @@ -312,8 +312,8 @@ class LocalShell(BaseModel): Union[ FunctionTool, FileSearchTool, - ComputerUseTool, ComputerTool, + ComputerUsePreviewTool, WebSearchTool, Mcp, CodeInterpreter, diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index e35d178d6e..c0f33c4513 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -16,9 +16,9 @@ from .apply_patch_tool_param import ApplyPatchToolParam from .file_search_tool_param import FileSearchToolParam from .tool_search_tool_param import ToolSearchToolParam -from .computer_use_tool_param import ComputerUseToolParam from .function_shell_tool_param import FunctionShellToolParam from .web_search_preview_tool_param import WebSearchPreviewToolParam +from .computer_use_preview_tool_param import ComputerUsePreviewToolParam from .container_network_policy_disabled_param import ContainerNetworkPolicyDisabledParam from .container_network_policy_allowlist_param import ContainerNetworkPolicyAllowlistParam @@ -311,8 +311,8 @@ class LocalShell(TypedDict, total=False): ToolParam: TypeAlias = Union[ FunctionToolParam, FileSearchToolParam, - ComputerUseToolParam, ComputerToolParam, + ComputerUsePreviewToolParam, WebSearchToolParam, Mcp, CodeInterpreter, From 0a4ca536f356aa23a021962b442d0c187559326d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:15:35 -0400 Subject: [PATCH 256/408] release: 2.27.0 (#2938) * chore(internal): codegen related update * codegen metadata * feat(api): api update * chore: match http protocol with ws protocol instead of wss * chore: use proper capitalization for WebSockets * chore(internal): codegen related update * feat(api): manual updates * feat(api): manual updates merge sora api changes * feat(api): sora api improvements: character api, video extensions/edits, higher resolution exports. * fix(api): repair merged videos resource * release: 2.27.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: Alex Chang --- .github/workflows/ci.yml | 8 +- .release-please-manifest.json | 2 +- .stats.yml | 8 +- CHANGELOG.md | 24 + api.md | 9 +- pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/resources/realtime/realtime.py | 34 +- src/openai/resources/responses/responses.py | 34 +- src/openai/resources/videos.py | 455 +++++++++++++++++- src/openai/types/__init__.py | 11 +- .../types/image_input_reference_param.py | 14 + .../types/responses/response_input_file.py | 6 - .../responses/response_input_file_content.py | 6 - .../response_input_file_content_param.py | 6 - .../responses/response_input_file_param.py | 6 - .../types/video_create_character_params.py | 17 + .../types/video_create_character_response.py | 18 + src/openai/types/video_create_params.py | 13 +- src/openai/types/video_edit_params.py | 28 ++ src/openai/types/video_extend_params.py | 35 ++ .../types/video_get_character_response.py | 18 + .../types/websocket_connection_options.py | 12 +- .../audio/test_transcriptions.py | 32 +- .../api_resources/audio/test_translations.py | 16 +- tests/api_resources/containers/test_files.py | 4 +- tests/api_resources/skills/test_versions.py | 4 +- tests/api_resources/test_files.py | 16 +- tests/api_resources/test_images.py | 56 +-- tests/api_resources/test_skills.py | 4 +- tests/api_resources/test_videos.py | 292 ++++++++++- tests/api_resources/uploads/test_parts.py | 16 +- tests/test_websocket_connection_options.py | 20 + 33 files changed, 1064 insertions(+), 164 deletions(-) create mode 100644 src/openai/types/image_input_reference_param.py create mode 100644 src/openai/types/video_create_character_params.py create mode 100644 src/openai/types/video_create_character_response.py create mode 100644 src/openai/types/video_edit_params.py create mode 100644 src/openai/types/video_extend_params.py create mode 100644 src/openai/types/video_get_character_response.py create mode 100644 tests/test_websocket_connection_options.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d087636a64..9c28de3543 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,14 +61,18 @@ jobs: run: rye build - name: Get GitHub OIDC Token - if: github.repository == 'stainless-sdks/openai-python' + if: |- + github.repository == 'stainless-sdks/openai-python' && + !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball - if: github.repository == 'stainless-sdks/openai-python' + if: |- + github.repository == 'stainless-sdks/openai-python' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1441304df6..4bdf489489 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.26.0" + ".": "2.27.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index bd550123f1..60deb7e24e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 148 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9c802d45a9bf2a896b5fd22ac22bba185e8a145bd40ed242df9bb87a05e954eb.yml -openapi_spec_hash: 97984ed69285e660b7d5c810c69ed449 -config_hash: 8240b8a7a7fc145a45b93bda435612d6 +configured_endpoints: 152 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-3e207c26eea3b15837c78ef2fe0e1c68937708fd0763971ce749c0bdb7db6376.yml +openapi_spec_hash: 626982004d5a594a822fa7883422efb4 +config_hash: 0dda4b3af379312c9c55467a5e1e1ec0 diff --git a/CHANGELOG.md b/CHANGELOG.md index c8508a0b1d..c750be23c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 2.27.0 (2026-03-13) + +Full Changelog: [v2.26.0...v2.27.0](https://github.com/openai/openai-python/compare/v2.26.0...v2.27.0) + +### Features + +* **api:** api update ([60ab24a](https://github.com/openai/openai-python/commit/60ab24ae722a7fa280eb4b2273da4ded1f930231)) +* **api:** manual updates ([b244b09](https://github.com/openai/openai-python/commit/b244b0946045aaa0dbfa8c0ce5164b64e1156834)) +* **api:** manual updates ([d806635](https://github.com/openai/openai-python/commit/d806635081a736cc81344bf1e62b57956a88d093)) +* **api:** sora api improvements: character api, video extensions/edits, higher resolution exports. ([58b70d3](https://github.com/openai/openai-python/commit/58b70d304a4b2cf70eae4db4b448d439fc8b8ba3)) + + +### Bug Fixes + +* **api:** repair merged videos resource ([742d8ee](https://github.com/openai/openai-python/commit/742d8ee1f969ee1bbb39ba9d799dcd5c480d8ddb)) + + +### Chores + +* **internal:** codegen related update ([4e6498e](https://github.com/openai/openai-python/commit/4e6498e2d222dd35d76bb397ba976ff53c852e12)) +* **internal:** codegen related update ([93af129](https://github.com/openai/openai-python/commit/93af129e8919de6d3aee19329c8bdef0532bd20a)) +* match http protocol with ws protocol instead of wss ([026f9de](https://github.com/openai/openai-python/commit/026f9de35d2aa74f35c91261eb5ea43d4ab1b8ba)) +* use proper capitalization for WebSockets ([a2f9b07](https://github.com/openai/openai-python/commit/a2f9b0722597627e8d01aa05c27a52015072726b)) + ## 2.26.0 (2026-03-05) Full Changelog: [v2.25.0...v2.26.0](https://github.com/openai/openai-python/compare/v2.25.0...v2.26.0) diff --git a/api.md b/api.md index a7981f7185..852df5bb8a 100644 --- a/api.md +++ b/api.md @@ -859,12 +859,15 @@ Types: ```python from openai.types import ( + ImageInputReferenceParam, Video, VideoCreateError, VideoModel, VideoSeconds, VideoSize, VideoDeleteResponse, + VideoCreateCharacterResponse, + VideoGetCharacterResponse, ) ``` @@ -874,7 +877,11 @@ Methods: - client.videos.retrieve(video_id) -> Video - client.videos.list(\*\*params) -> SyncConversationCursorPage[Video] - client.videos.delete(video_id) -> VideoDeleteResponse +- client.videos.create_character(\*\*params) -> VideoCreateCharacterResponse - client.videos.download_content(video_id, \*\*params) -> HttpxBinaryResponseContent +- client.videos.edit(\*\*params) -> Video +- client.videos.extend(\*\*params) -> Video +- client.videos.get_character(character_id) -> VideoGetCharacterResponse - client.videos.remix(video_id, \*\*params) -> Video - client.videos.create_and_poll(\*args) -> Video - +- client.videos.poll(\*args) -> Video diff --git a/pyproject.toml b/pyproject.toml index 41738c8c5e..1419825193 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.26.0" +version = "2.27.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 2feccec170..755c8a9f5c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.26.0" # x-release-please-version +__version__ = "2.27.0" # x-release-please-version diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 44f14cd3aa..73a87fc2e7 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -41,7 +41,7 @@ AsyncClientSecretsWithStreamingResponse, ) from ...types.realtime import session_update_event_param -from ...types.websocket_connection_options import WebsocketConnectionOptions +from ...types.websocket_connection_options import WebSocketConnectionOptions from ...types.realtime.realtime_client_event import RealtimeClientEvent from ...types.realtime.realtime_server_event import RealtimeServerEvent from ...types.realtime.conversation_item_param import ConversationItemParam @@ -49,8 +49,8 @@ from ...types.realtime.realtime_response_create_params_param import RealtimeResponseCreateParamsParam if TYPE_CHECKING: - from websockets.sync.client import ClientConnection as WebsocketConnection - from websockets.asyncio.client import ClientConnection as AsyncWebsocketConnection + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection from ..._client import OpenAI, AsyncOpenAI @@ -96,7 +96,7 @@ def connect( model: str | Omit = omit, extra_query: Query = {}, extra_headers: Headers = {}, - websocket_connection_options: WebsocketConnectionOptions = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, ) -> RealtimeConnectionManager: """ 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. @@ -156,7 +156,7 @@ def connect( model: str | Omit = omit, extra_query: Query = {}, extra_headers: Headers = {}, - websocket_connection_options: WebsocketConnectionOptions = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, ) -> AsyncRealtimeConnectionManager: """ 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. @@ -240,9 +240,9 @@ class AsyncRealtimeConnection: conversation: AsyncRealtimeConversationResource output_audio_buffer: AsyncRealtimeOutputAudioBufferResource - _connection: AsyncWebsocketConnection + _connection: AsyncWebSocketConnection - def __init__(self, connection: AsyncWebsocketConnection) -> None: + def __init__(self, connection: AsyncWebSocketConnection) -> None: self._connection = connection self.session = AsyncRealtimeSessionResource(self) @@ -281,7 +281,7 @@ async def recv_bytes(self) -> bytes: then you can call `.parse_event(data)`. """ message = await self._connection.recv(decode=False) - log.debug(f"Received websocket message: %s", message) + log.debug(f"Received WebSocket message: %s", message) return message async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: @@ -334,7 +334,7 @@ def __init__( model: str | Omit = omit, extra_query: Query, extra_headers: Headers, - websocket_connection_options: WebsocketConnectionOptions, + websocket_connection_options: WebSocketConnectionOptions, ) -> None: self.__client = client self.__call_id = call_id @@ -408,7 +408,9 @@ def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) else: - base_url = self.__client._base_url.copy_with(scheme="wss") + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" return base_url.copy_with(raw_path=merge_raw_path) @@ -429,9 +431,9 @@ class RealtimeConnection: conversation: RealtimeConversationResource output_audio_buffer: RealtimeOutputAudioBufferResource - _connection: WebsocketConnection + _connection: WebSocketConnection - def __init__(self, connection: WebsocketConnection) -> None: + def __init__(self, connection: WebSocketConnection) -> None: self._connection = connection self.session = RealtimeSessionResource(self) @@ -470,7 +472,7 @@ def recv_bytes(self) -> bytes: then you can call `.parse_event(data)`. """ message = self._connection.recv(decode=False) - log.debug(f"Received websocket message: %s", message) + log.debug(f"Received WebSocket message: %s", message) return message def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: @@ -523,7 +525,7 @@ def __init__( model: str | Omit = omit, extra_query: Query, extra_headers: Headers, - websocket_connection_options: WebsocketConnectionOptions, + websocket_connection_options: WebSocketConnectionOptions, ) -> None: self.__client = client self.__call_id = call_id @@ -597,7 +599,9 @@ def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) else: - base_url = self.__client._base_url.copy_with(scheme="wss") + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" return base_url.copy_with(raw_path=merge_raw_path) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 5d34909fd1..12f1e1aea1 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -58,7 +58,7 @@ from ...types.responses.parsed_response import ParsedResponse from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager from ...types.responses.compacted_response import CompactedResponse -from ...types.websocket_connection_options import WebsocketConnectionOptions +from ...types.websocket_connection_options import WebSocketConnectionOptions from ...types.responses.response_includable import ResponseIncludable from ...types.shared_params.responses_model import ResponsesModel from ...types.responses.response_input_param import ResponseInputParam @@ -71,8 +71,8 @@ from ...types.responses.responses_client_event_param import ResponsesClientEventParam if TYPE_CHECKING: - from websockets.sync.client import ClientConnection as WebsocketConnection - from websockets.asyncio.client import ClientConnection as AsyncWebsocketConnection + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection from ..._client import OpenAI, AsyncOpenAI @@ -1730,7 +1730,7 @@ def connect( self, extra_query: Query = {}, extra_headers: Headers = {}, - websocket_connection_options: WebsocketConnectionOptions = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, ) -> ResponsesConnectionManager: """Connect to a persistent Responses API WebSocket. @@ -3397,7 +3397,7 @@ def connect( self, extra_query: Query = {}, extra_headers: Headers = {}, - websocket_connection_options: WebsocketConnectionOptions = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, ) -> AsyncResponsesConnectionManager: """Connect to a persistent Responses API WebSocket. @@ -3576,9 +3576,9 @@ class AsyncResponsesConnection: response: AsyncResponsesResponseResource - _connection: AsyncWebsocketConnection + _connection: AsyncWebSocketConnection - def __init__(self, connection: AsyncWebsocketConnection) -> None: + def __init__(self, connection: AsyncWebSocketConnection) -> None: self._connection = connection self.response = AsyncResponsesResponseResource(self) @@ -3613,7 +3613,7 @@ async def recv_bytes(self) -> bytes: then you can call `.parse_event(data)`. """ message = await self._connection.recv(decode=False) - log.debug(f"Received websocket message: %s", message) + log.debug(f"Received WebSocket message: %s", message) return message async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: @@ -3665,7 +3665,7 @@ def __init__( client: AsyncOpenAI, extra_query: Query, extra_headers: Headers, - websocket_connection_options: WebsocketConnectionOptions, + websocket_connection_options: WebSocketConnectionOptions, ) -> None: self.__client = client self.__connection: AsyncResponsesConnection | None = None @@ -3723,7 +3723,9 @@ def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) else: - base_url = self.__client._base_url.copy_with(scheme="wss") + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" return base_url.copy_with(raw_path=merge_raw_path) @@ -3740,9 +3742,9 @@ class ResponsesConnection: response: ResponsesResponseResource - _connection: WebsocketConnection + _connection: WebSocketConnection - def __init__(self, connection: WebsocketConnection) -> None: + def __init__(self, connection: WebSocketConnection) -> None: self._connection = connection self.response = ResponsesResponseResource(self) @@ -3777,7 +3779,7 @@ def recv_bytes(self) -> bytes: then you can call `.parse_event(data)`. """ message = self._connection.recv(decode=False) - log.debug(f"Received websocket message: %s", message) + log.debug(f"Received WebSocket message: %s", message) return message def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: @@ -3829,7 +3831,7 @@ def __init__( client: OpenAI, extra_query: Query, extra_headers: Headers, - websocket_connection_options: WebsocketConnectionOptions, + websocket_connection_options: WebSocketConnectionOptions, ) -> None: self.__client = client self.__connection: ResponsesConnection | None = None @@ -3887,7 +3889,9 @@ def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) else: - base_url = self.__client._base_url.copy_with(scheme="wss") + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" return base_url.copy_with(raw_path=merge_raw_path) diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 51df6da4d3..f387f55824 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -11,9 +11,12 @@ from ..types import ( VideoSize, VideoSeconds, + video_edit_params, video_list_params, video_remix_params, video_create_params, + video_extend_params, + video_create_character_params, video_download_content_params, ) from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given @@ -36,6 +39,8 @@ from ..types.video_seconds import VideoSeconds from ..types.video_model_param import VideoModelParam from ..types.video_delete_response import VideoDeleteResponse +from ..types.video_get_character_response import VideoGetCharacterResponse +from ..types.video_create_character_response import VideoCreateCharacterResponse __all__ = ["Videos", "AsyncVideos"] @@ -64,7 +69,7 @@ def create( self, *, prompt: str, - input_reference: FileTypes | Omit = omit, + input_reference: video_create_params.InputReference | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, @@ -81,7 +86,7 @@ def create( Args: prompt: Text prompt that describes the video to generate. - input_reference: Optional multipart reference asset that guides generation. + input_reference: Optional reference asset upload or reference object that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`. @@ -109,11 +114,10 @@ def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( "/videos", body=maybe_transform(body, video_create_params.VideoCreateParams), @@ -128,7 +132,7 @@ def create_and_poll( self, *, prompt: str, - input_reference: FileTypes | Omit = omit, + input_reference: video_create_params.InputReference | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, @@ -315,6 +319,55 @@ def delete( cast_to=VideoDeleteResponse, ) + def create_character( + self, + *, + name: str, + video: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VideoCreateCharacterResponse: + """ + Create a character from an uploaded video. + + Args: + name: Display name for this API character. + + video: Video file used to create a character. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "name": name, + "video": video, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos/characters", + body=maybe_transform(body, video_create_character_params.VideoCreateCharacterParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VideoCreateCharacterResponse, + ) + def download_content( self, video_id: str, @@ -358,6 +411,143 @@ def download_content( cast_to=_legacy_response.HttpxBinaryResponseContent, ) + def edit( + self, + *, + prompt: str, + video: video_edit_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a new video generation job by editing a source video or existing + generated video. + + Args: + prompt: Text prompt that describes how to edit the source video. + + video: Reference to the completed video to edit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "prompt": prompt, + "video": video, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos/edits", + body=maybe_transform(body, video_edit_params.VideoEditParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + def extend( + self, + *, + prompt: str, + seconds: VideoSeconds, + video: video_extend_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create an extension of a completed video. + + Args: + prompt: Updated text prompt that directs the extension generation. + + seconds: Length of the newly generated extension segment in seconds (allowed values: 4, + 8, 12, 16, 20). + + video: Reference to the completed video to extend. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "prompt": prompt, + "seconds": seconds, + "video": video, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return self._post( + "/videos/extensions", + body=maybe_transform(body, video_extend_params.VideoExtendParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + def get_character( + self, + character_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VideoGetCharacterResponse: + """ + Fetch a character. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not character_id: + raise ValueError(f"Expected a non-empty value for `character_id` but received {character_id!r}") + return self._get( + f"/videos/characters/{character_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VideoGetCharacterResponse, + ) + def remix( self, video_id: str, @@ -420,7 +610,7 @@ async def create( self, *, prompt: str, - input_reference: FileTypes | Omit = omit, + input_reference: video_create_params.InputReference | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, @@ -437,7 +627,7 @@ async def create( Args: prompt: Text prompt that describes the video to generate. - input_reference: Optional multipart reference asset that guides generation. + input_reference: Optional reference asset upload or reference object that guides generation. model: The video generation model to use (allowed values: sora-2, sora-2-pro). Defaults to `sora-2`. @@ -465,11 +655,10 @@ async def create( } ) files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) - if files: - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( "/videos", body=await async_maybe_transform(body, video_create_params.VideoCreateParams), @@ -484,7 +673,7 @@ async def create_and_poll( self, *, prompt: str, - input_reference: FileTypes | Omit = omit, + input_reference: video_create_params.InputReference | Omit = omit, model: VideoModelParam | Omit = omit, seconds: VideoSeconds | Omit = omit, size: VideoSize | Omit = omit, @@ -671,6 +860,55 @@ async def delete( cast_to=VideoDeleteResponse, ) + async def create_character( + self, + *, + name: str, + video: FileTypes, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VideoCreateCharacterResponse: + """ + Create a character from an uploaded video. + + Args: + name: Display name for this API character. + + video: Video file used to create a character. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "name": name, + "video": video, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos/characters", + body=await async_maybe_transform(body, video_create_character_params.VideoCreateCharacterParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VideoCreateCharacterResponse, + ) + async def download_content( self, video_id: str, @@ -716,6 +954,143 @@ async def download_content( cast_to=_legacy_response.HttpxBinaryResponseContent, ) + async def edit( + self, + *, + prompt: str, + video: video_edit_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create a new video generation job by editing a source video or existing + generated video. + + Args: + prompt: Text prompt that describes how to edit the source video. + + video: Reference to the completed video to edit. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "prompt": prompt, + "video": video, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos/edits", + body=await async_maybe_transform(body, video_edit_params.VideoEditParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + async def extend( + self, + *, + prompt: str, + seconds: VideoSeconds, + video: video_extend_params.Video, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Video: + """ + Create an extension of a completed video. + + Args: + prompt: Updated text prompt that directs the extension generation. + + seconds: Length of the newly generated extension segment in seconds (allowed values: 4, + 8, 12, 16, 20). + + video: Reference to the completed video to extend. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + body = deepcopy_minimal( + { + "prompt": prompt, + "seconds": seconds, + "video": video, + } + ) + files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) + # It should be noted that the actual Content-Type header that will be + # sent to the server will contain a `boundary` parameter, e.g. + # multipart/form-data; boundary=---abc-- + extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} + return await self._post( + "/videos/extensions", + body=await async_maybe_transform(body, video_extend_params.VideoExtendParams), + files=files, + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Video, + ) + + async def get_character( + self, + character_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VideoGetCharacterResponse: + """ + Fetch a character. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not character_id: + raise ValueError(f"Expected a non-empty value for `character_id` but received {character_id!r}") + return await self._get( + f"/videos/characters/{character_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=VideoGetCharacterResponse, + ) + async def remix( self, video_id: str, @@ -770,9 +1145,21 @@ def __init__(self, videos: Videos) -> None: self.delete = _legacy_response.to_raw_response_wrapper( videos.delete, ) + self.create_character = _legacy_response.to_raw_response_wrapper( + videos.create_character, + ) self.download_content = _legacy_response.to_raw_response_wrapper( videos.download_content, ) + self.edit = _legacy_response.to_raw_response_wrapper( + videos.edit, + ) + self.extend = _legacy_response.to_raw_response_wrapper( + videos.extend, + ) + self.get_character = _legacy_response.to_raw_response_wrapper( + videos.get_character, + ) self.remix = _legacy_response.to_raw_response_wrapper( videos.remix, ) @@ -794,9 +1181,21 @@ def __init__(self, videos: AsyncVideos) -> None: self.delete = _legacy_response.async_to_raw_response_wrapper( videos.delete, ) + self.create_character = _legacy_response.async_to_raw_response_wrapper( + videos.create_character, + ) self.download_content = _legacy_response.async_to_raw_response_wrapper( videos.download_content, ) + self.edit = _legacy_response.async_to_raw_response_wrapper( + videos.edit, + ) + self.extend = _legacy_response.async_to_raw_response_wrapper( + videos.extend, + ) + self.get_character = _legacy_response.async_to_raw_response_wrapper( + videos.get_character, + ) self.remix = _legacy_response.async_to_raw_response_wrapper( videos.remix, ) @@ -818,10 +1217,22 @@ def __init__(self, videos: Videos) -> None: self.delete = to_streamed_response_wrapper( videos.delete, ) + self.create_character = to_streamed_response_wrapper( + videos.create_character, + ) self.download_content = to_custom_streamed_response_wrapper( videos.download_content, StreamedBinaryAPIResponse, ) + self.edit = to_streamed_response_wrapper( + videos.edit, + ) + self.extend = to_streamed_response_wrapper( + videos.extend, + ) + self.get_character = to_streamed_response_wrapper( + videos.get_character, + ) self.remix = to_streamed_response_wrapper( videos.remix, ) @@ -843,10 +1254,22 @@ def __init__(self, videos: AsyncVideos) -> None: self.delete = async_to_streamed_response_wrapper( videos.delete, ) + self.create_character = async_to_streamed_response_wrapper( + videos.create_character, + ) self.download_content = async_to_custom_streamed_response_wrapper( videos.download_content, AsyncStreamedBinaryAPIResponse, ) + self.edit = async_to_streamed_response_wrapper( + videos.edit, + ) + self.extend = async_to_streamed_response_wrapper( + videos.extend, + ) + self.get_character = async_to_streamed_response_wrapper( + videos.get_character, + ) self.remix = async_to_streamed_response_wrapper( videos.remix, ) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 9190bc146c..d8dbea71ad 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -56,6 +56,7 @@ from .completion_choice import CompletionChoice as CompletionChoice from .image_edit_params import ImageEditParams as ImageEditParams from .skill_list_params import SkillListParams as SkillListParams +from .video_edit_params import VideoEditParams as VideoEditParams from .video_list_params import VideoListParams as VideoListParams from .video_model_param import VideoModelParam as VideoModelParam from .eval_create_params import EvalCreateParams as EvalCreateParams @@ -68,6 +69,7 @@ from .skill_create_params import SkillCreateParams as SkillCreateParams from .skill_update_params import SkillUpdateParams as SkillUpdateParams from .video_create_params import VideoCreateParams as VideoCreateParams +from .video_extend_params import VideoExtendParams as VideoExtendParams from .batch_request_counts import BatchRequestCounts as BatchRequestCounts from .eval_create_response import EvalCreateResponse as EvalCreateResponse from .eval_delete_response import EvalDeleteResponse as EvalDeleteResponse @@ -98,16 +100,23 @@ from .vector_store_search_params import VectorStoreSearchParams as VectorStoreSearchParams from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams from .container_retrieve_response import ContainerRetrieveResponse as ContainerRetrieveResponse +from .image_input_reference_param import ImageInputReferenceParam as ImageInputReferenceParam from .moderation_text_input_param import ModerationTextInputParam as ModerationTextInputParam from .file_chunking_strategy_param import FileChunkingStrategyParam as FileChunkingStrategyParam from .vector_store_search_response import VectorStoreSearchResponse as VectorStoreSearchResponse -from .websocket_connection_options import WebsocketConnectionOptions as WebsocketConnectionOptions +from .video_get_character_response import VideoGetCharacterResponse as VideoGetCharacterResponse +from .websocket_connection_options import ( + WebSocketConnectionOptions as WebSocketConnectionOptions, + WebsocketConnectionOptions as WebsocketConnectionOptions, +) from .image_create_variation_params import ImageCreateVariationParams as ImageCreateVariationParams from .image_gen_partial_image_event import ImageGenPartialImageEvent as ImageGenPartialImageEvent from .static_file_chunking_strategy import StaticFileChunkingStrategy as StaticFileChunkingStrategy +from .video_create_character_params import VideoCreateCharacterParams as VideoCreateCharacterParams from .video_download_content_params import VideoDownloadContentParams as VideoDownloadContentParams from .eval_custom_data_source_config import EvalCustomDataSourceConfig as EvalCustomDataSourceConfig from .image_edit_partial_image_event import ImageEditPartialImageEvent as ImageEditPartialImageEvent +from .video_create_character_response import VideoCreateCharacterResponse as VideoCreateCharacterResponse from .moderation_image_url_input_param import ModerationImageURLInputParam as ModerationImageURLInputParam from .auto_file_chunking_strategy_param import AutoFileChunkingStrategyParam as AutoFileChunkingStrategyParam from .moderation_multi_modal_input_param import ModerationMultiModalInputParam as ModerationMultiModalInputParam diff --git a/src/openai/types/image_input_reference_param.py b/src/openai/types/image_input_reference_param.py new file mode 100644 index 0000000000..1065632910 --- /dev/null +++ b/src/openai/types/image_input_reference_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ImageInputReferenceParam"] + + +class ImageInputReferenceParam(TypedDict, total=False): + file_id: str + + image_url: str + """A fully qualified URL or base64-encoded data URL.""" diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py index 97cc176f35..3e5fb70c5f 100644 --- a/src/openai/types/responses/response_input_file.py +++ b/src/openai/types/responses/response_input_file.py @@ -14,12 +14,6 @@ class ResponseInputFile(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" - detail: Optional[Literal["low", "high"]] = None - """The detail level of the file to be sent to the model. - - One of `high` or `low`. Defaults to `high`. - """ - file_data: Optional[str] = None """The content of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_content.py b/src/openai/types/responses/response_input_file_content.py index 7b1c76bff7..f0dfef55d0 100644 --- a/src/openai/types/responses/response_input_file_content.py +++ b/src/openai/types/responses/response_input_file_content.py @@ -14,12 +14,6 @@ class ResponseInputFileContent(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" - detail: Optional[Literal["high", "low"]] = None - """The detail level of the file to be sent to the model. - - One of `high` or `low`. Defaults to `high`. - """ - file_data: Optional[str] = None """The base64-encoded data of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_content_param.py b/src/openai/types/responses/response_input_file_content_param.py index 73e8acd27b..376f6c7a45 100644 --- a/src/openai/types/responses/response_input_file_content_param.py +++ b/src/openai/types/responses/response_input_file_content_param.py @@ -14,12 +14,6 @@ class ResponseInputFileContentParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" - detail: Literal["high", "low"] - """The detail level of the file to be sent to the model. - - One of `high` or `low`. Defaults to `high`. - """ - file_data: Optional[str] """The base64-encoded data of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py index 25eec2fe01..8b5da20245 100644 --- a/src/openai/types/responses/response_input_file_param.py +++ b/src/openai/types/responses/response_input_file_param.py @@ -14,12 +14,6 @@ class ResponseInputFileParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" - detail: Literal["low", "high"] - """The detail level of the file to be sent to the model. - - One of `high` or `low`. Defaults to `high`. - """ - file_data: str """The content of the file to be sent to the model.""" diff --git a/src/openai/types/video_create_character_params.py b/src/openai/types/video_create_character_params.py new file mode 100644 index 0000000000..ef671e598f --- /dev/null +++ b/src/openai/types/video_create_character_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import FileTypes + +__all__ = ["VideoCreateCharacterParams"] + + +class VideoCreateCharacterParams(TypedDict, total=False): + name: Required[str] + """Display name for this API character.""" + + video: Required[FileTypes] + """Video file used to create a character.""" diff --git a/src/openai/types/video_create_character_response.py b/src/openai/types/video_create_character_response.py new file mode 100644 index 0000000000..e3a65a0200 --- /dev/null +++ b/src/openai/types/video_create_character_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["VideoCreateCharacterResponse"] + + +class VideoCreateCharacterResponse(BaseModel): + id: Optional[str] = None + """Identifier for the character creation cameo.""" + + created_at: int + """Unix timestamp (in seconds) when the character was created.""" + + name: Optional[str] = None + """Display name for the character.""" diff --git a/src/openai/types/video_create_params.py b/src/openai/types/video_create_params.py index 282b1db429..641ac7db45 100644 --- a/src/openai/types/video_create_params.py +++ b/src/openai/types/video_create_params.py @@ -2,22 +2,24 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict from .._types import FileTypes from .video_size import VideoSize from .video_seconds import VideoSeconds from .video_model_param import VideoModelParam +from .image_input_reference_param import ImageInputReferenceParam -__all__ = ["VideoCreateParams"] +__all__ = ["VideoCreateParams", "InputReference"] class VideoCreateParams(TypedDict, total=False): prompt: Required[str] """Text prompt that describes the video to generate.""" - input_reference: FileTypes - """Optional multipart reference asset that guides generation.""" + input_reference: InputReference + """Optional reference asset upload or reference object that guides generation.""" model: VideoModelParam """The video generation model to use (allowed values: sora-2, sora-2-pro). @@ -33,3 +35,6 @@ class VideoCreateParams(TypedDict, total=False): Output resolution formatted as width x height (allowed values: 720x1280, 1280x720, 1024x1792, 1792x1024). Defaults to 720x1280. """ + + +InputReference: TypeAlias = Union[FileTypes, ImageInputReferenceParam] diff --git a/src/openai/types/video_edit_params.py b/src/openai/types/video_edit_params.py new file mode 100644 index 0000000000..8d3b15fc6f --- /dev/null +++ b/src/openai/types/video_edit_params.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from .._types import FileTypes + +__all__ = ["VideoEditParams", "Video", "VideoVideoReferenceInputParam"] + + +class VideoEditParams(TypedDict, total=False): + prompt: Required[str] + """Text prompt that describes how to edit the source video.""" + + video: Required[Video] + """Reference to the completed video to edit.""" + + +class VideoVideoReferenceInputParam(TypedDict, total=False): + """Reference to the completed video.""" + + id: Required[str] + """The identifier of the completed video.""" + + +Video: TypeAlias = Union[FileTypes, VideoVideoReferenceInputParam] diff --git a/src/openai/types/video_extend_params.py b/src/openai/types/video_extend_params.py new file mode 100644 index 0000000000..65be4b5270 --- /dev/null +++ b/src/openai/types/video_extend_params.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, TypeAlias, TypedDict + +from .._types import FileTypes +from .video_seconds import VideoSeconds + +__all__ = ["VideoExtendParams", "Video", "VideoVideoReferenceInputParam"] + + +class VideoExtendParams(TypedDict, total=False): + prompt: Required[str] + """Updated text prompt that directs the extension generation.""" + + seconds: Required[VideoSeconds] + """ + Length of the newly generated extension segment in seconds (allowed values: 4, + 8, 12, 16, 20). + """ + + video: Required[Video] + """Reference to the completed video to extend.""" + + +class VideoVideoReferenceInputParam(TypedDict, total=False): + """Reference to the completed video.""" + + id: Required[str] + """The identifier of the completed video.""" + + +Video: TypeAlias = Union[FileTypes, VideoVideoReferenceInputParam] diff --git a/src/openai/types/video_get_character_response.py b/src/openai/types/video_get_character_response.py new file mode 100644 index 0000000000..df6202ed03 --- /dev/null +++ b/src/openai/types/video_get_character_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["VideoGetCharacterResponse"] + + +class VideoGetCharacterResponse(BaseModel): + id: Optional[str] = None + """Identifier for the character creation cameo.""" + + created_at: int + """Unix timestamp (in seconds) when the character was created.""" + + name: Optional[str] = None + """Display name for the character.""" diff --git a/src/openai/types/websocket_connection_options.py b/src/openai/types/websocket_connection_options.py index 40fd24ab03..519e434124 100644 --- a/src/openai/types/websocket_connection_options.py +++ b/src/openai/types/websocket_connection_options.py @@ -3,15 +3,17 @@ from __future__ import annotations from typing import TYPE_CHECKING -from typing_extensions import Sequence, TypedDict +from typing_extensions import Sequence, TypeAlias, TypedDict + +__all__ = ["WebSocketConnectionOptions", "WebsocketConnectionOptions"] if TYPE_CHECKING: from websockets import Subprotocol from websockets.extensions import ClientExtensionFactory -class WebsocketConnectionOptions(TypedDict, total=False): - """Websocket connection options copied from `websockets`. +class WebSocketConnectionOptions(TypedDict, total=False): + """WebSocket connection options copied from `websockets`. For example: https://websockets.readthedocs.io/en/stable/reference/asyncio/client.html#websockets.asyncio.client.connect """ @@ -34,3 +36,7 @@ class WebsocketConnectionOptions(TypedDict, total=False): write_limit: int | tuple[int, int | None] """High-water mark of write buffer in bytes. It is passed to set_write_buffer_limits(). It defaults to 32 KiB. You may pass a (high, low) tuple to set the high-water and low-water marks.""" + + +# Backward compatibility for pre-rename imports. +WebsocketConnectionOptions: TypeAlias = WebSocketConnectionOptions diff --git a/tests/api_resources/audio/test_transcriptions.py b/tests/api_resources/audio/test_transcriptions.py index b5eaa4be1f..b4525937b4 100644 --- a/tests/api_resources/audio/test_transcriptions.py +++ b/tests/api_resources/audio/test_transcriptions.py @@ -20,7 +20,7 @@ class TestTranscriptions: @parametrize def test_method_create_overload_1(self, client: OpenAI) -> None: transcription = client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @@ -28,7 +28,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: transcription = client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", chunking_strategy="auto", include=["logprobs"], @@ -46,7 +46,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_1(self, client: OpenAI) -> None: response = client.audio.transcriptions.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", ) @@ -58,7 +58,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: with client.audio.transcriptions.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", ) as response: assert not response.is_closed @@ -72,7 +72,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_overload_2(self, client: OpenAI) -> None: transcription_stream = client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, ) @@ -81,7 +81,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: transcription_stream = client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, chunking_strategy="auto", @@ -99,7 +99,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_2(self, client: OpenAI) -> None: response = client.audio.transcriptions.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, ) @@ -111,7 +111,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: with client.audio.transcriptions.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, ) as response: @@ -132,7 +132,7 @@ class TestAsyncTranscriptions: @parametrize async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: transcription = await async_client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", ) assert_matches_type(TranscriptionCreateResponse, transcription, path=["response"]) @@ -140,7 +140,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: transcription = await async_client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", chunking_strategy="auto", include=["logprobs"], @@ -158,7 +158,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.audio.transcriptions.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", ) @@ -170,7 +170,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.audio.transcriptions.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", ) as response: assert not response.is_closed @@ -184,7 +184,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe @parametrize async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: transcription_stream = await async_client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, ) @@ -193,7 +193,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: transcription_stream = await async_client.audio.transcriptions.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, chunking_strategy="auto", @@ -211,7 +211,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: response = await async_client.audio.transcriptions.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, ) @@ -223,7 +223,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: async with async_client.audio.transcriptions.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", model="gpt-4o-transcribe", stream=True, ) as response: diff --git a/tests/api_resources/audio/test_translations.py b/tests/api_resources/audio/test_translations.py index ead69e9369..d6848d1278 100644 --- a/tests/api_resources/audio/test_translations.py +++ b/tests/api_resources/audio/test_translations.py @@ -20,7 +20,7 @@ class TestTranslations: @parametrize def test_method_create(self, client: OpenAI) -> None: translation = client.audio.translations.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", ) assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @@ -28,7 +28,7 @@ def test_method_create(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: translation = client.audio.translations.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", prompt="prompt", response_format="json", @@ -39,7 +39,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.audio.translations.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", ) @@ -51,7 +51,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: with client.audio.translations.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", ) as response: assert not response.is_closed @@ -71,7 +71,7 @@ class TestAsyncTranslations: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: translation = await async_client.audio.translations.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", ) assert_matches_type(TranslationCreateResponse, translation, path=["response"]) @@ -79,7 +79,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: translation = await async_client.audio.translations.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", prompt="prompt", response_format="json", @@ -90,7 +90,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.audio.translations.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", ) @@ -102,7 +102,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.audio.translations.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", model="whisper-1", ) as response: assert not response.is_closed diff --git a/tests/api_resources/containers/test_files.py b/tests/api_resources/containers/test_files.py index f9d82d005c..9d47785894 100644 --- a/tests/api_resources/containers/test_files.py +++ b/tests/api_resources/containers/test_files.py @@ -33,7 +33,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: file = client.containers.files.create( container_id="container_id", - file=b"raw file contents", + file=b"Example data", file_id="file_id", ) assert_matches_type(FileCreateResponse, file, path=["response"]) @@ -230,7 +230,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: file = await async_client.containers.files.create( container_id="container_id", - file=b"raw file contents", + file=b"Example data", file_id="file_id", ) assert_matches_type(FileCreateResponse, file, path=["response"]) diff --git a/tests/api_resources/skills/test_versions.py b/tests/api_resources/skills/test_versions.py index 5f4dcbf51d..40c807354a 100644 --- a/tests/api_resources/skills/test_versions.py +++ b/tests/api_resources/skills/test_versions.py @@ -30,7 +30,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: version = client.skills.versions.create( skill_id="skill_123", default=True, - files=[b"raw file contents"], + files=[b"Example data"], ) assert_matches_type(SkillVersion, version, path=["response"]) @@ -227,7 +227,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> version = await async_client.skills.versions.create( skill_id="skill_123", default=True, - files=[b"raw file contents"], + files=[b"Example data"], ) assert_matches_type(SkillVersion, version, path=["response"]) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 67c809f155..940ac97022 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -26,7 +26,7 @@ class TestFiles: @parametrize def test_method_create(self, client: OpenAI) -> None: file = client.files.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", ) assert_matches_type(FileObject, file, path=["response"]) @@ -34,7 +34,7 @@ def test_method_create(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: file = client.files.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", expires_after={ "anchor": "created_at", @@ -46,7 +46,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.files.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", ) @@ -58,7 +58,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: with client.files.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", ) as response: assert not response.is_closed @@ -279,7 +279,7 @@ class TestAsyncFiles: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: file = await async_client.files.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", ) assert_matches_type(FileObject, file, path=["response"]) @@ -287,7 +287,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: file = await async_client.files.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", expires_after={ "anchor": "created_at", @@ -299,7 +299,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.files.with_raw_response.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", ) @@ -311,7 +311,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.files.with_streaming_response.create( - file=b"raw file contents", + file=b"Example data", purpose="assistants", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 99fe77d8e0..9862b79c65 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -20,14 +20,14 @@ class TestImages: @parametrize def test_method_create_variation(self, client: OpenAI) -> None: image = client.images.create_variation( - image=b"raw file contents", + image=b"Example data", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: image = client.images.create_variation( - image=b"raw file contents", + image=b"Example data", model="string", n=1, response_format="url", @@ -39,7 +39,7 @@ def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_variation(self, client: OpenAI) -> None: response = client.images.with_raw_response.create_variation( - image=b"raw file contents", + image=b"Example data", ) assert response.is_closed is True @@ -50,7 +50,7 @@ def test_raw_response_create_variation(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_variation(self, client: OpenAI) -> None: with client.images.with_streaming_response.create_variation( - image=b"raw file contents", + image=b"Example data", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -63,7 +63,7 @@ def test_streaming_response_create_variation(self, client: OpenAI) -> None: @parametrize def test_method_edit_overload_1(self, client: OpenAI) -> None: image = client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -71,11 +71,11 @@ def test_method_edit_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_edit_with_all_params_overload_1(self, client: OpenAI) -> None: image = client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", background="transparent", input_fidelity="high", - mask=b"raw file contents", + mask=b"Example data", model="string", n=1, output_compression=100, @@ -92,7 +92,7 @@ def test_method_edit_with_all_params_overload_1(self, client: OpenAI) -> None: @parametrize def test_raw_response_edit_overload_1(self, client: OpenAI) -> None: response = client.images.with_raw_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", ) @@ -104,7 +104,7 @@ def test_raw_response_edit_overload_1(self, client: OpenAI) -> None: @parametrize def test_streaming_response_edit_overload_1(self, client: OpenAI) -> None: with client.images.with_streaming_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", ) as response: assert not response.is_closed @@ -118,7 +118,7 @@ def test_streaming_response_edit_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_edit_overload_2(self, client: OpenAI) -> None: image_stream = client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, ) @@ -127,12 +127,12 @@ def test_method_edit_overload_2(self, client: OpenAI) -> None: @parametrize def test_method_edit_with_all_params_overload_2(self, client: OpenAI) -> None: image_stream = client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, background="transparent", input_fidelity="high", - mask=b"raw file contents", + mask=b"Example data", model="string", n=1, output_compression=100, @@ -148,7 +148,7 @@ def test_method_edit_with_all_params_overload_2(self, client: OpenAI) -> None: @parametrize def test_raw_response_edit_overload_2(self, client: OpenAI) -> None: response = client.images.with_raw_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, ) @@ -160,7 +160,7 @@ def test_raw_response_edit_overload_2(self, client: OpenAI) -> None: @parametrize def test_streaming_response_edit_overload_2(self, client: OpenAI) -> None: with client.images.with_streaming_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, ) as response: @@ -285,14 +285,14 @@ class TestAsyncImages: @parametrize async def test_method_create_variation(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.create_variation( - image=b"raw file contents", + image=b"Example data", ) assert_matches_type(ImagesResponse, image, path=["response"]) @parametrize async def test_method_create_variation_with_all_params(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.create_variation( - image=b"raw file contents", + image=b"Example data", model="string", n=1, response_format="url", @@ -304,7 +304,7 @@ async def test_method_create_variation_with_all_params(self, async_client: Async @parametrize async def test_raw_response_create_variation(self, async_client: AsyncOpenAI) -> None: response = await async_client.images.with_raw_response.create_variation( - image=b"raw file contents", + image=b"Example data", ) assert response.is_closed is True @@ -315,7 +315,7 @@ async def test_raw_response_create_variation(self, async_client: AsyncOpenAI) -> @parametrize async def test_streaming_response_create_variation(self, async_client: AsyncOpenAI) -> None: async with async_client.images.with_streaming_response.create_variation( - image=b"raw file contents", + image=b"Example data", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -328,7 +328,7 @@ async def test_streaming_response_create_variation(self, async_client: AsyncOpen @parametrize async def test_method_edit_overload_1(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", ) assert_matches_type(ImagesResponse, image, path=["response"]) @@ -336,11 +336,11 @@ async def test_method_edit_overload_1(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_edit_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", background="transparent", input_fidelity="high", - mask=b"raw file contents", + mask=b"Example data", model="string", n=1, output_compression=100, @@ -357,7 +357,7 @@ async def test_method_edit_with_all_params_overload_1(self, async_client: AsyncO @parametrize async def test_raw_response_edit_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.images.with_raw_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", ) @@ -369,7 +369,7 @@ async def test_raw_response_edit_overload_1(self, async_client: AsyncOpenAI) -> @parametrize async def test_streaming_response_edit_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.images.with_streaming_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", ) as response: assert not response.is_closed @@ -383,7 +383,7 @@ async def test_streaming_response_edit_overload_1(self, async_client: AsyncOpenA @parametrize async def test_method_edit_overload_2(self, async_client: AsyncOpenAI) -> None: image_stream = await async_client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, ) @@ -392,12 +392,12 @@ async def test_method_edit_overload_2(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_edit_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: image_stream = await async_client.images.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, background="transparent", input_fidelity="high", - mask=b"raw file contents", + mask=b"Example data", model="string", n=1, output_compression=100, @@ -413,7 +413,7 @@ async def test_method_edit_with_all_params_overload_2(self, async_client: AsyncO @parametrize async def test_raw_response_edit_overload_2(self, async_client: AsyncOpenAI) -> None: response = await async_client.images.with_raw_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, ) @@ -425,7 +425,7 @@ async def test_raw_response_edit_overload_2(self, async_client: AsyncOpenAI) -> @parametrize async def test_streaming_response_edit_overload_2(self, async_client: AsyncOpenAI) -> None: async with async_client.images.with_streaming_response.edit( - image=b"raw file contents", + image=b"Example data", prompt="A cute baby sea otter wearing a beret", stream=True, ) as response: diff --git a/tests/api_resources/test_skills.py b/tests/api_resources/test_skills.py index fb4cea92ce..6708fb3cbf 100644 --- a/tests/api_resources/test_skills.py +++ b/tests/api_resources/test_skills.py @@ -26,7 +26,7 @@ def test_method_create(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: skill = client.skills.create( - files=[b"raw file contents"], + files=[b"Example data"], ) assert_matches_type(Skill, skill, path=["response"]) @@ -216,7 +216,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: skill = await async_client.skills.create( - files=[b"raw file contents"], + files=[b"Example data"], ) assert_matches_type(Skill, skill, path=["response"]) diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py index b785e03ca5..73acf6d05d 100644 --- a/tests/api_resources/test_videos.py +++ b/tests/api_resources/test_videos.py @@ -15,6 +15,8 @@ from openai.types import ( Video, VideoDeleteResponse, + VideoGetCharacterResponse, + VideoCreateCharacterResponse, ) from openai._utils import assert_signatures_in_sync from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage @@ -38,7 +40,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: video = client.videos.create( prompt="x", - input_reference=b"raw file contents", + input_reference=b"Example data", model="string", seconds="4", size="720x1280", @@ -179,6 +181,40 @@ def test_path_params_delete(self, client: OpenAI) -> None: "", ) + @parametrize + def test_method_create_character(self, client: OpenAI) -> None: + video = client.videos.create_character( + name="x", + video=b"Example data", + ) + assert_matches_type(VideoCreateCharacterResponse, video, path=["response"]) + + @parametrize + def test_raw_response_create_character(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.create_character( + name="x", + video=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(VideoCreateCharacterResponse, video, path=["response"]) + + @parametrize + def test_streaming_response_create_character(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.create_character( + name="x", + video=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(VideoCreateCharacterResponse, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize @pytest.mark.respx(base_url=base_url) def test_method_download_content(self, client: OpenAI, respx_mock: MockRouter) -> None: @@ -237,6 +273,115 @@ def test_path_params_download_content(self, client: OpenAI) -> None: video_id="", ) + @parametrize + def test_method_edit(self, client: OpenAI) -> None: + video = client.videos.edit( + prompt="x", + video=b"Example data", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_raw_response_edit(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.edit( + prompt="x", + video=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_streaming_response_edit(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.edit( + prompt="x", + video=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_extend(self, client: OpenAI) -> None: + video = client.videos.extend( + prompt="x", + seconds="4", + video=b"Example data", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_raw_response_extend(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.extend( + prompt="x", + seconds="4", + video=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + def test_streaming_response_extend(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.extend( + prompt="x", + seconds="4", + video=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_get_character(self, client: OpenAI) -> None: + video = client.videos.get_character( + "char_123", + ) + assert_matches_type(VideoGetCharacterResponse, video, path=["response"]) + + @parametrize + def test_raw_response_get_character(self, client: OpenAI) -> None: + response = client.videos.with_raw_response.get_character( + "char_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(VideoGetCharacterResponse, video, path=["response"]) + + @parametrize + def test_streaming_response_get_character(self, client: OpenAI) -> None: + with client.videos.with_streaming_response.get_character( + "char_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = response.parse() + assert_matches_type(VideoGetCharacterResponse, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get_character(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `character_id` but received ''"): + client.videos.with_raw_response.get_character( + "", + ) + @parametrize def test_method_remix(self, client: OpenAI) -> None: video = client.videos.remix( @@ -296,7 +441,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: video = await async_client.videos.create( prompt="x", - input_reference=b"raw file contents", + input_reference=b"Example data", model="string", seconds="4", size="720x1280", @@ -437,6 +582,40 @@ async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: "", ) + @parametrize + async def test_method_create_character(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.create_character( + name="x", + video=b"Example data", + ) + assert_matches_type(VideoCreateCharacterResponse, video, path=["response"]) + + @parametrize + async def test_raw_response_create_character(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.create_character( + name="x", + video=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(VideoCreateCharacterResponse, video, path=["response"]) + + @parametrize + async def test_streaming_response_create_character(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.create_character( + name="x", + video=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(VideoCreateCharacterResponse, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_download_content(self, async_client: AsyncOpenAI, respx_mock: MockRouter) -> None: @@ -497,6 +676,115 @@ async def test_path_params_download_content(self, async_client: AsyncOpenAI) -> video_id="", ) + @parametrize + async def test_method_edit(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.edit( + prompt="x", + video=b"Example data", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_raw_response_edit(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.edit( + prompt="x", + video=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_streaming_response_edit(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.edit( + prompt="x", + video=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_extend(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.extend( + prompt="x", + seconds="4", + video=b"Example data", + ) + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_raw_response_extend(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.extend( + prompt="x", + seconds="4", + video=b"Example data", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(Video, video, path=["response"]) + + @parametrize + async def test_streaming_response_extend(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.extend( + prompt="x", + seconds="4", + video=b"Example data", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(Video, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_get_character(self, async_client: AsyncOpenAI) -> None: + video = await async_client.videos.get_character( + "char_123", + ) + assert_matches_type(VideoGetCharacterResponse, video, path=["response"]) + + @parametrize + async def test_raw_response_get_character(self, async_client: AsyncOpenAI) -> None: + response = await async_client.videos.with_raw_response.get_character( + "char_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + video = response.parse() + assert_matches_type(VideoGetCharacterResponse, video, path=["response"]) + + @parametrize + async def test_streaming_response_get_character(self, async_client: AsyncOpenAI) -> None: + async with async_client.videos.with_streaming_response.get_character( + "char_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + video = await response.parse() + assert_matches_type(VideoGetCharacterResponse, video, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get_character(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `character_id` but received ''"): + await async_client.videos.with_raw_response.get_character( + "", + ) + @parametrize async def test_method_remix(self, async_client: AsyncOpenAI) -> None: video = await async_client.videos.remix( diff --git a/tests/api_resources/uploads/test_parts.py b/tests/api_resources/uploads/test_parts.py index 191d3a1b04..b5956d263b 100644 --- a/tests/api_resources/uploads/test_parts.py +++ b/tests/api_resources/uploads/test_parts.py @@ -21,7 +21,7 @@ class TestParts: def test_method_create(self, client: OpenAI) -> None: part = client.uploads.parts.create( upload_id="upload_abc123", - data=b"raw file contents", + data=b"Example data", ) assert_matches_type(UploadPart, part, path=["response"]) @@ -29,7 +29,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.uploads.parts.with_raw_response.create( upload_id="upload_abc123", - data=b"raw file contents", + data=b"Example data", ) assert response.is_closed is True @@ -41,7 +41,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.uploads.parts.with_streaming_response.create( upload_id="upload_abc123", - data=b"raw file contents", + data=b"Example data", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -56,7 +56,7 @@ def test_path_params_create(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): client.uploads.parts.with_raw_response.create( upload_id="", - data=b"raw file contents", + data=b"Example data", ) @@ -69,7 +69,7 @@ class TestAsyncParts: async def test_method_create(self, async_client: AsyncOpenAI) -> None: part = await async_client.uploads.parts.create( upload_id="upload_abc123", - data=b"raw file contents", + data=b"Example data", ) assert_matches_type(UploadPart, part, path=["response"]) @@ -77,7 +77,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.uploads.parts.with_raw_response.create( upload_id="upload_abc123", - data=b"raw file contents", + data=b"Example data", ) assert response.is_closed is True @@ -89,7 +89,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.uploads.parts.with_streaming_response.create( upload_id="upload_abc123", - data=b"raw file contents", + data=b"Example data", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -104,5 +104,5 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"): await async_client.uploads.parts.with_raw_response.create( upload_id="", - data=b"raw file contents", + data=b"Example data", ) diff --git a/tests/test_websocket_connection_options.py b/tests/test_websocket_connection_options.py new file mode 100644 index 0000000000..122ee10078 --- /dev/null +++ b/tests/test_websocket_connection_options.py @@ -0,0 +1,20 @@ +from openai import types +from openai.types import websocket_connection_options + + +def test_submodule_alias_is_preserved() -> None: + assert ( + websocket_connection_options.WebsocketConnectionOptions + is websocket_connection_options.WebSocketConnectionOptions + ) + + +def test_public_types_alias_is_preserved() -> None: + assert types.WebsocketConnectionOptions is types.WebSocketConnectionOptions + + +def test_beta_realtime_import_still_works_with_old_alias() -> None: + from openai.resources.beta.realtime.realtime import Realtime, AsyncRealtime + + assert Realtime.__name__ == "Realtime" + assert AsyncRealtime.__name__ == "AsyncRealtime" From c1a87d945359f6ed4bfaa5328bb2e9ab3ff67874 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:49:07 +0000 Subject: [PATCH 257/408] feat(api): custom voices --- .stats.yml | 6 ++--- src/openai/resources/audio/speech.py | 18 ++++++------- .../types/audio/speech_create_params.py | 25 ++++++++++++----- .../types/chat/chat_completion_audio_param.py | 24 ++++++++++++----- .../realtime/realtime_audio_config_output.py | 27 +++++++++++++------ .../realtime_audio_config_output_param.py | 25 ++++++++++++----- .../realtime_response_create_audio_output.py | 26 +++++++++++++----- ...time_response_create_audio_output_param.py | 24 +++++++++++++---- tests/api_resources/audio/test_speech.py | 16 +++++------ tests/api_resources/chat/test_completions.py | 8 +++--- tests/api_resources/realtime/test_calls.py | 8 +++--- .../realtime/test_client_secrets.py | 4 +-- 12 files changed, 141 insertions(+), 70 deletions(-) diff --git a/.stats.yml b/.stats.yml index 60deb7e24e..3e6307edaf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-3e207c26eea3b15837c78ef2fe0e1c68937708fd0763971ce749c0bdb7db6376.yml -openapi_spec_hash: 626982004d5a594a822fa7883422efb4 -config_hash: 0dda4b3af379312c9c55467a5e1e1ec0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-cb3e4451108eed58d59cff25bf77ec0dc960ec9c6f3dba68f90e7a9847c09d21.yml +openapi_spec_hash: dec6d9be64a5ba8f474a1f2a7a4fafef +config_hash: e922f01e25accd07d8fd3641c37fbd62 diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index f937321baa..80dbb44077 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -52,9 +52,7 @@ def create( *, input: str, model: Union[str, SpeechModel], - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] - ], + voice: speech_create_params.Voice, instructions: str | Omit = omit, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, speed: float | Omit = omit, @@ -80,8 +78,9 @@ def create( voice: The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, - `shimmer`, `verse`, `marin`, and `cedar`. Previews of the voices are available - in the + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the + voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). instructions: Control the voice of your generated audio with additional instructions. Does not @@ -153,9 +152,7 @@ async def create( *, input: str, model: Union[str, SpeechModel], - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] - ], + voice: speech_create_params.Voice, instructions: str | Omit = omit, response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, speed: float | Omit = omit, @@ -181,8 +178,9 @@ async def create( voice: The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `onyx`, `nova`, `sage`, - `shimmer`, `verse`, `marin`, and `cedar`. Previews of the voices are available - in the + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Previews of the + voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). instructions: Control the voice of your generated audio with additional instructions. Does not diff --git a/src/openai/types/audio/speech_create_params.py b/src/openai/types/audio/speech_create_params.py index 417df5b218..1c0472ea85 100644 --- a/src/openai/types/audio/speech_create_params.py +++ b/src/openai/types/audio/speech_create_params.py @@ -3,11 +3,11 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .speech_model import SpeechModel -__all__ = ["SpeechCreateParams"] +__all__ = ["SpeechCreateParams", "Voice", "VoiceID"] class SpeechCreateParams(TypedDict, total=False): @@ -20,14 +20,13 @@ class SpeechCreateParams(TypedDict, total=False): `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, or `gpt-4o-mini-tts-2025-12-15`. """ - voice: Required[ - Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] - ] + voice: Required[Voice] """The voice to use when generating the audio. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, - `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. - Previews of the voices are available in the + `fable`, `onyx`, `nova`, `sage`, `shimmer`, `verse`, `marin`, and `cedar`. You + may also provide a custom voice object with an `id`, for example + `{ "id": "voice_1234" }`. Previews of the voices are available in the [Text to speech guide](https://platform.openai.com/docs/guides/text-to-speech#voice-options). """ @@ -55,3 +54,15 @@ class SpeechCreateParams(TypedDict, total=False): Supported formats are `sse` and `audio`. `sse` is not supported for `tts-1` or `tts-1-hd`. """ + + +class VoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID +] diff --git a/src/openai/types/chat/chat_completion_audio_param.py b/src/openai/types/chat/chat_completion_audio_param.py index 1a73bb0c7e..fe64ba49aa 100644 --- a/src/openai/types/chat/chat_completion_audio_param.py +++ b/src/openai/types/chat/chat_completion_audio_param.py @@ -3,9 +3,21 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["ChatCompletionAudioParam"] +__all__ = ["ChatCompletionAudioParam", "Voice", "VoiceID"] + + +class VoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID +] class ChatCompletionAudioParam(TypedDict, total=False): @@ -21,11 +33,11 @@ class ChatCompletionAudioParam(TypedDict, total=False): Must be one of `wav`, `mp3`, `flac`, `opus`, or `pcm16`. """ - voice: Required[ - Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] - ] + voice: Required[Voice] """The voice the model uses to respond. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, - `fable`, `nova`, `onyx`, `sage`, `shimmer`, `marin`, and `cedar`. + `fable`, `nova`, `onyx`, `sage`, `shimmer`, `marin`, and `cedar`. You may also + provide a custom voice object with an `id`, for example + `{ "id": "voice_1234" }`. """ diff --git a/src/openai/types/realtime/realtime_audio_config_output.py b/src/openai/types/realtime/realtime_audio_config_output.py index 2922405f63..143cef67a5 100644 --- a/src/openai/types/realtime/realtime_audio_config_output.py +++ b/src/openai/types/realtime/realtime_audio_config_output.py @@ -1,12 +1,24 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from ..._models import BaseModel from .realtime_audio_formats import RealtimeAudioFormats -__all__ = ["RealtimeAudioConfigOutput"] +__all__ = ["RealtimeAudioConfigOutput", "Voice", "VoiceID"] + + +class VoiceID(BaseModel): + """Custom voice reference.""" + + id: str + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID +] class RealtimeAudioConfigOutput(BaseModel): @@ -24,13 +36,12 @@ class RealtimeAudioConfigOutput(BaseModel): generated, it's also possible to prompt the model to speak faster or slower. """ - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None - ] = None + voice: Optional[Voice] = None """The voice the model uses to respond. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, - `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the - session once the model has responded with audio at least once. We recommend - `marin` and `cedar` for best quality. + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. """ diff --git a/src/openai/types/realtime/realtime_audio_config_output_param.py b/src/openai/types/realtime/realtime_audio_config_output_param.py index d04fd3a303..5d920f69a6 100644 --- a/src/openai/types/realtime/realtime_audio_config_output_param.py +++ b/src/openai/types/realtime/realtime_audio_config_output_param.py @@ -3,11 +3,23 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .realtime_audio_formats_param import RealtimeAudioFormatsParam -__all__ = ["RealtimeAudioConfigOutputParam"] +__all__ = ["RealtimeAudioConfigOutputParam", "Voice", "VoiceID"] + + +class VoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +Voice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], VoiceID +] class RealtimeAudioConfigOutputParam(TypedDict, total=False): @@ -25,11 +37,12 @@ class RealtimeAudioConfigOutputParam(TypedDict, total=False): generated, it's also possible to prompt the model to speak faster or slower. """ - voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + voice: Voice """The voice the model uses to respond. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, - `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the - session once the model has responded with audio at least once. We recommend - `marin` and `cedar` for best quality. + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. """ diff --git a/src/openai/types/realtime/realtime_response_create_audio_output.py b/src/openai/types/realtime/realtime_response_create_audio_output.py index db02511ab1..7848357236 100644 --- a/src/openai/types/realtime/realtime_response_create_audio_output.py +++ b/src/openai/types/realtime/realtime_response_create_audio_output.py @@ -1,26 +1,38 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional -from typing_extensions import Literal +from typing_extensions import Literal, TypeAlias from ..._models import BaseModel from .realtime_audio_formats import RealtimeAudioFormats -__all__ = ["RealtimeResponseCreateAudioOutput", "Output"] +__all__ = ["RealtimeResponseCreateAudioOutput", "Output", "OutputVoice", "OutputVoiceID"] + + +class OutputVoiceID(BaseModel): + """Custom voice reference.""" + + id: str + """The custom voice ID, e.g. `voice_1234`.""" + + +OutputVoice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], OutputVoiceID +] class Output(BaseModel): format: Optional[RealtimeAudioFormats] = None """The format of the output audio.""" - voice: Union[ - str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], None - ] = None + voice: Optional[OutputVoice] = None """The voice the model uses to respond. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, - `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the - session once the model has responded with audio at least once. + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. """ diff --git a/src/openai/types/realtime/realtime_response_create_audio_output_param.py b/src/openai/types/realtime/realtime_response_create_audio_output_param.py index 22787ad106..bb930f5488 100644 --- a/src/openai/types/realtime/realtime_response_create_audio_output_param.py +++ b/src/openai/types/realtime/realtime_response_create_audio_output_param.py @@ -3,23 +3,37 @@ from __future__ import annotations from typing import Union -from typing_extensions import Literal, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .realtime_audio_formats_param import RealtimeAudioFormatsParam -__all__ = ["RealtimeResponseCreateAudioOutputParam", "Output"] +__all__ = ["RealtimeResponseCreateAudioOutputParam", "Output", "OutputVoice", "OutputVoiceID"] + + +class OutputVoiceID(TypedDict, total=False): + """Custom voice reference.""" + + id: Required[str] + """The custom voice ID, e.g. `voice_1234`.""" + + +OutputVoice: TypeAlias = Union[ + str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], OutputVoiceID +] class Output(TypedDict, total=False): format: RealtimeAudioFormatsParam """The format of the output audio.""" - voice: Union[str, Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"]] + voice: OutputVoice """The voice the model uses to respond. Supported built-in voices are `alloy`, `ash`, `ballad`, `coral`, `echo`, `sage`, - `shimmer`, `verse`, `marin`, and `cedar`. Voice cannot be changed during the - session once the model has responded with audio at least once. + `shimmer`, `verse`, `marin`, and `cedar`. You may also provide a custom voice + object with an `id`, for example `{ "id": "voice_1234" }`. Voice cannot be + changed during the session once the model has responded with audio at least + once. We recommend `marin` and `cedar` for best quality. """ diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index 2c77f38949..a42c77126d 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -28,7 +28,7 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: speech = client.audio.speech.create( input="string", model="string", - voice="ash", + voice="string", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} @@ -40,7 +40,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou speech = client.audio.speech.create( input="string", model="string", - voice="ash", + voice="string", instructions="instructions", response_format="mp3", speed=0.25, @@ -57,7 +57,7 @@ def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> No response = client.audio.speech.with_raw_response.create( input="string", model="string", - voice="ash", + voice="string", ) assert response.is_closed is True @@ -72,7 +72,7 @@ def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) with client.audio.speech.with_streaming_response.create( input="string", model="string", - voice="ash", + voice="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -95,7 +95,7 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRo speech = await async_client.audio.speech.create( input="string", model="string", - voice="ash", + voice="string", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} @@ -107,7 +107,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re speech = await async_client.audio.speech.create( input="string", model="string", - voice="ash", + voice="string", instructions="instructions", response_format="mp3", speed=0.25, @@ -124,7 +124,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: response = await async_client.audio.speech.with_raw_response.create( input="string", model="string", - voice="ash", + voice="string", ) assert response.is_closed is True @@ -139,7 +139,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_ async with async_client.audio.speech.with_streaming_response.create( input="string", model="string", - voice="ash", + voice="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 995b752e11..c55c132697 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -48,7 +48,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: model="gpt-5.4", audio={ "format": "wav", - "voice": "ash", + "voice": "string", }, frequency_penalty=-2, function_call="none", @@ -182,7 +182,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: stream=True, audio={ "format": "wav", - "voice": "ash", + "voice": "string", }, frequency_penalty=-2, function_call="none", @@ -491,7 +491,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn model="gpt-5.4", audio={ "format": "wav", - "voice": "ash", + "voice": "string", }, frequency_penalty=-2, function_call="none", @@ -625,7 +625,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn stream=True, audio={ "format": "wav", - "voice": "ash", + "voice": "string", }, frequency_penalty=-2, function_call="none", diff --git a/tests/api_resources/realtime/test_calls.py b/tests/api_resources/realtime/test_calls.py index 9bb6ef3faf..9e2810841d 100644 --- a/tests/api_resources/realtime/test_calls.py +++ b/tests/api_resources/realtime/test_calls.py @@ -67,7 +67,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou "type": "audio/pcm", }, "speed": 0.25, - "voice": "ash", + "voice": "string", }, }, "include": ["item.input_audio_transcription.logprobs"], @@ -166,7 +166,7 @@ def test_method_accept_with_all_params(self, client: OpenAI) -> None: "type": "audio/pcm", }, "speed": 0.25, - "voice": "ash", + "voice": "string", }, }, include=["item.input_audio_transcription.logprobs"], @@ -405,7 +405,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re "type": "audio/pcm", }, "speed": 0.25, - "voice": "ash", + "voice": "string", }, }, "include": ["item.input_audio_transcription.logprobs"], @@ -504,7 +504,7 @@ async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> "type": "audio/pcm", }, "speed": 0.25, - "voice": "ash", + "voice": "string", }, }, include=["item.input_audio_transcription.logprobs"], diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py index 17762771ac..bfa0deac55 100644 --- a/tests/api_resources/realtime/test_client_secrets.py +++ b/tests/api_resources/realtime/test_client_secrets.py @@ -59,7 +59,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "type": "audio/pcm", }, "speed": 0.25, - "voice": "ash", + "voice": "string", }, }, "include": ["item.input_audio_transcription.logprobs"], @@ -155,7 +155,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "type": "audio/pcm", }, "speed": 0.25, - "voice": "ash", + "voice": "string", }, }, "include": ["item.input_audio_transcription.logprobs"], From fc7f598efa70c6bef5ad15d1a8c9dfcd0fbd33fa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:49:41 +0000 Subject: [PATCH 258/408] release: 2.28.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4bdf489489..fa4d75253f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.27.0" + ".": "2.28.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c750be23c4..dfc7ddca89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.28.0 (2026-03-13) + +Full Changelog: [v2.27.0...v2.28.0](https://github.com/openai/openai-python/compare/v2.27.0...v2.28.0) + +### Features + +* **api:** custom voices ([50dc060](https://github.com/openai/openai-python/commit/50dc060b55767615419219ef567d31210517e613)) + ## 2.27.0 (2026-03-13) Full Changelog: [v2.26.0...v2.27.0](https://github.com/openai/openai-python/compare/v2.26.0...v2.27.0) diff --git a/pyproject.toml b/pyproject.toml index 1419825193..ea82be70c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.27.0" +version = "2.28.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 755c8a9f5c..45ae8eb37a 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.27.0" # x-release-please-version +__version__ = "2.28.0" # x-release-please-version From a499adcd6d9b31b87f8257b2e85c781f0ea17749 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 21:12:01 +0000 Subject: [PATCH 259/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 3e6307edaf..5d6c5c38a7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-cb3e4451108eed58d59cff25bf77ec0dc960ec9c6f3dba68f90e7a9847c09d21.yml openapi_spec_hash: dec6d9be64a5ba8f474a1f2a7a4fafef -config_hash: e922f01e25accd07d8fd3641c37fbd62 +config_hash: 1cb55f2105c291f6f4721d06655d40f0 From c9ccb8fcaba323e10d8117f4565c9ef79602c1f1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2026 01:19:29 +0000 Subject: [PATCH 260/408] feat(api): add /v1/videos endpoint to batches create method --- .stats.yml | 4 ++-- src/openai/resources/batches.py | 16 ++++++++++------ src/openai/types/batch_create_params.py | 9 +++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5d6c5c38a7..1f91787668 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-cb3e4451108eed58d59cff25bf77ec0dc960ec9c6f3dba68f90e7a9847c09d21.yml -openapi_spec_hash: dec6d9be64a5ba8f474a1f2a7a4fafef +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bb3e130670aa251441ebf4857acd20c836da88350785a91be9d9af4a4ca25e98.yml +openapi_spec_hash: 45b78734bc16e9658175ca758c608a1e config_hash: 1cb55f2105c291f6f4721d06655d40f0 diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 005a32870e..c269b93e2c 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -56,6 +56,7 @@ def create( "/v1/moderations", "/v1/images/generations", "/v1/images/edits", + "/v1/videos", ], input_file_id: str, metadata: Optional[Metadata] | Omit = omit, @@ -76,9 +77,10 @@ def create( endpoint: The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, - `/v1/moderations`, `/v1/images/generations`, and `/v1/images/edits` are - supported. Note that `/v1/embeddings` batches are also restricted to a maximum - of 50,000 embedding inputs across all requests in the batch. + `/v1/moderations`, `/v1/images/generations`, `/v1/images/edits`, and + `/v1/videos` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. @@ -282,6 +284,7 @@ async def create( "/v1/moderations", "/v1/images/generations", "/v1/images/edits", + "/v1/videos", ], input_file_id: str, metadata: Optional[Metadata] | Omit = omit, @@ -302,9 +305,10 @@ async def create( endpoint: The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, `/v1/completions`, - `/v1/moderations`, `/v1/images/generations`, and `/v1/images/edits` are - supported. Note that `/v1/embeddings` batches are also restricted to a maximum - of 50,000 embedding inputs across all requests in the batch. + `/v1/moderations`, `/v1/images/generations`, `/v1/images/edits`, and + `/v1/videos` are supported. Note that `/v1/embeddings` batches are also + restricted to a maximum of 50,000 embedding inputs across all requests in the + batch. input_file_id: The ID of an uploaded file that contains requests for the new batch. diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py index 1bcd48aace..97bd2c67ed 100644 --- a/src/openai/types/batch_create_params.py +++ b/src/openai/types/batch_create_params.py @@ -26,15 +26,16 @@ class BatchCreateParams(TypedDict, total=False): "/v1/moderations", "/v1/images/generations", "/v1/images/edits", + "/v1/videos", ] ] """The endpoint to be used for all requests in the batch. Currently `/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`, - `/v1/completions`, `/v1/moderations`, `/v1/images/generations`, and - `/v1/images/edits` are supported. Note that `/v1/embeddings` batches are also - restricted to a maximum of 50,000 embedding inputs across all requests in the - batch. + `/v1/completions`, `/v1/moderations`, `/v1/images/generations`, + `/v1/images/edits`, and `/v1/videos` are supported. Note that `/v1/embeddings` + batches are also restricted to a maximum of 50,000 embedding inputs across all + requests in the batch. """ input_file_id: Required[str] From f857bdc3ad7bf72c247dfa0c25b3422758e9d15d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2026 02:18:11 +0000 Subject: [PATCH 261/408] feat(api): add defer_loading field to ToolFunction --- .stats.yml | 4 ++-- src/openai/types/responses/namespace_tool.py | 3 +++ src/openai/types/responses/namespace_tool_param.py | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1f91787668..3205b5bfa4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bb3e130670aa251441ebf4857acd20c836da88350785a91be9d9af4a4ca25e98.yml -openapi_spec_hash: 45b78734bc16e9658175ca758c608a1e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2ab0731e8c94b7c783b55939d2d2a46c2594c1da2ec444c543e17a9eea6d9164.yml +openapi_spec_hash: 5557d4cd48565e8b98dfcb96a5804965 config_hash: 1cb55f2105c291f6f4721d06655d40f0 diff --git a/src/openai/types/responses/namespace_tool.py b/src/openai/types/responses/namespace_tool.py index 2c311dbe17..88f76a9732 100644 --- a/src/openai/types/responses/namespace_tool.py +++ b/src/openai/types/responses/namespace_tool.py @@ -15,6 +15,9 @@ class ToolFunction(BaseModel): type: Literal["function"] + defer_loading: Optional[bool] = None + """Whether this function should be deferred and discovered via tool search.""" + description: Optional[str] = None parameters: Optional[object] = None diff --git a/src/openai/types/responses/namespace_tool_param.py b/src/openai/types/responses/namespace_tool_param.py index 4bda2ecd83..cb1e5e17f4 100644 --- a/src/openai/types/responses/namespace_tool_param.py +++ b/src/openai/types/responses/namespace_tool_param.py @@ -15,6 +15,9 @@ class ToolFunction(TypedDict, total=False): type: Required[Literal["function"]] + defer_loading: bool + """Whether this function should be deferred and discovered via tool search.""" + description: Optional[str] parameters: Optional[object] From 28bda04ca2bc849e188d68e0bfe3de9cae682a81 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:49:55 +0000 Subject: [PATCH 262/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 3205b5bfa4..db892f8ebb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2ab0731e8c94b7c783b55939d2d2a46c2594c1da2ec444c543e17a9eea6d9164.yml openapi_spec_hash: 5557d4cd48565e8b98dfcb96a5804965 -config_hash: 1cb55f2105c291f6f4721d06655d40f0 +config_hash: 1e4cbbae1fcd20c0b729289210c04d1a From fdf7f83d2b5171c70e97ec164a482cd675cd05a7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 16:07:36 +0000 Subject: [PATCH 263/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index db892f8ebb..b27e3589a8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2ab0731e8c94b7c783b55939d2d2a46c2594c1da2ec444c543e17a9eea6d9164.yml openapi_spec_hash: 5557d4cd48565e8b98dfcb96a5804965 -config_hash: 1e4cbbae1fcd20c0b729289210c04d1a +config_hash: 96fbf82cf74a44ccd513f5acf0956ffd From 02b30719a6eb40bba58cd86c85c1f63a671786fd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:34:07 +0000 Subject: [PATCH 264/408] fix(pydantic): do not pass `by_alias` unless set --- src/openai/_compat.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/openai/_compat.py b/src/openai/_compat.py index 020ffeb2ca..340c91a6d0 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime -from typing_extensions import Self, Literal +from typing_extensions import Self, Literal, TypedDict import pydantic from pydantic.fields import FieldInfo @@ -131,6 +131,10 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: return model.model_dump_json(indent=indent) +class _ModelDumpKwargs(TypedDict, total=False): + by_alias: bool + + def model_dump( model: pydantic.BaseModel, *, @@ -142,6 +146,9 @@ def model_dump( by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + kwargs: _ModelDumpKwargs = {} + if by_alias is not None: + kwargs["by_alias"] = by_alias return model.model_dump( mode=mode, exclude=exclude, @@ -149,7 +156,7 @@ def model_dump( exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, - by_alias=by_alias, + **kwargs, ) return cast( "dict[str, Any]", From 11f50b68f3ce05e2299dd27a0e7af293210ca919 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:03:06 +0000 Subject: [PATCH 265/408] fix(deps): bump minimum typing-extensions version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ea82be70c4..f70513a8be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.11, <5", + "typing-extensions>=4.11, <5", "typing-extensions>=4.14, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", From cf8e9e7beaedc7bfa2b142c1aa12672efd68024c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:47:20 +0000 Subject: [PATCH 266/408] chore(internal): tweak CI branches --- .github/workflows/ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c28de3543..3ad9f39206 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' From bc96310eddc1aa1c22c490fc5c5d99daf60b55e8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 22:08:41 +0000 Subject: [PATCH 267/408] feat(api): add in and nin operators to ComparisonFilter type --- .stats.yml | 4 ++-- src/openai/types/shared/comparison_filter.py | 2 +- src/openai/types/shared_params/comparison_filter.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index b27e3589a8..45b3b3f373 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2ab0731e8c94b7c783b55939d2d2a46c2594c1da2ec444c543e17a9eea6d9164.yml -openapi_spec_hash: 5557d4cd48565e8b98dfcb96a5804965 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5a660d5b485aae5061d55301f7c8522654a533c7d7d9596c4da54f0e481d8880.yml +openapi_spec_hash: 50297cf7c625ec3c4bb0bc0f5a9d318a config_hash: 96fbf82cf74a44ccd513f5acf0956ffd diff --git a/src/openai/types/shared/comparison_filter.py b/src/openai/types/shared/comparison_filter.py index 852cac1738..57c26cd016 100644 --- a/src/openai/types/shared/comparison_filter.py +++ b/src/openai/types/shared/comparison_filter.py @@ -16,7 +16,7 @@ class ComparisonFilter(BaseModel): key: str """The key to compare against the value.""" - type: Literal["eq", "ne", "gt", "gte", "lt", "lte"] + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"] """ Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`. diff --git a/src/openai/types/shared_params/comparison_filter.py b/src/openai/types/shared_params/comparison_filter.py index 363688e467..005f4d1f0d 100644 --- a/src/openai/types/shared_params/comparison_filter.py +++ b/src/openai/types/shared_params/comparison_filter.py @@ -18,7 +18,7 @@ class ComparisonFilter(TypedDict, total=False): key: Required[str] """The key to compare against the value.""" - type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte"]] + type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]] """ Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`. From 2c67256406df9c9d4367a0dfcda63dbe84cd1522 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:36:00 +0000 Subject: [PATCH 268/408] feat(api): 5.4 nano and mini model slugs --- .stats.yml | 4 ++-- src/openai/resources/responses/responses.py | 8 ++++++++ src/openai/types/responses/response_compact_params.py | 4 ++++ src/openai/types/shared/chat_model.py | 4 ++++ src/openai/types/shared_params/chat_model.py | 4 ++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 45b3b3f373..b47f21a4ac 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5a660d5b485aae5061d55301f7c8522654a533c7d7d9596c4da54f0e481d8880.yml -openapi_spec_hash: 50297cf7c625ec3c4bb0bc0f5a9d318a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-55ef7034334e938c30656a404ce5e21466103be87542a796425346299f450404.yml +openapi_spec_hash: 4a5bfd2ee4ad47f5b7cf6f1ad08d5d7f config_hash: 96fbf82cf74a44ccd513f5acf0956ffd diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 12f1e1aea1..6d57f86313 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1568,6 +1568,10 @@ def compact( model: Union[ Literal[ "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", @@ -3235,6 +3239,10 @@ async def compact( model: Union[ Literal[ "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index 95b5da437d..0b163a9e78 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -15,6 +15,10 @@ class ResponseCompactParams(TypedDict, total=False): Union[ Literal[ "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index b86bd28ef9..501a22a8bd 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -6,6 +6,10 @@ ChatModel: TypeAlias = Literal[ "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index 2a45c8f423..17eaacd905 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -8,6 +8,10 @@ ChatModel: TypeAlias = Literal[ "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", "gpt-5.3-chat-latest", "gpt-5.2", "gpt-5.2-2025-12-11", From acd0c54d8a68efeedde0e5b4e6c310eef1ce7867 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:36:45 +0000 Subject: [PATCH 269/408] release: 2.29.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fa4d75253f..3a5058bfce 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.28.0" + ".": "2.29.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dfc7ddca89..eea76f7709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 2.29.0 (2026-03-17) + +Full Changelog: [v2.28.0...v2.29.0](https://github.com/openai/openai-python/compare/v2.28.0...v2.29.0) + +### Features + +* **api:** 5.4 nano and mini model slugs ([3b45666](https://github.com/openai/openai-python/commit/3b456661f77ca3196aceb5ab3350664a63481114)) +* **api:** add /v1/videos endpoint to batches create method ([c0e7a16](https://github.com/openai/openai-python/commit/c0e7a161a996854021e9eb69ea2a60ca0d08047f)) +* **api:** add defer_loading field to ToolFunction ([3167595](https://github.com/openai/openai-python/commit/3167595432bdda2f90721901d30ad316db49323e)) +* **api:** add in and nin operators to ComparisonFilter type ([664f02b](https://github.com/openai/openai-python/commit/664f02b051af84e1ca3fa313981ec72fdea269b3)) + + +### Bug Fixes + +* **deps:** bump minimum typing-extensions version ([a2fb2ca](https://github.com/openai/openai-python/commit/a2fb2ca55142c6658a18be7bd1392a01f5a83f35)) +* **pydantic:** do not pass `by_alias` unless set ([8ebe8fb](https://github.com/openai/openai-python/commit/8ebe8fbcb011c6a005a715cae50c6400a8596ee0)) + + +### Chores + +* **internal:** tweak CI branches ([96ccc3c](https://github.com/openai/openai-python/commit/96ccc3cca35645fd3140f99b0fc8e55545065212)) + ## 2.28.0 (2026-03-13) Full Changelog: [v2.27.0...v2.28.0](https://github.com/openai/openai-python/compare/v2.27.0...v2.28.0) diff --git a/pyproject.toml b/pyproject.toml index f70513a8be..46a72007df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.28.0" +version = "2.29.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 45ae8eb37a..b8a1b37e13 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.28.0" # x-release-please-version +__version__ = "2.29.0" # x-release-please-version From 9445eac0a424fee4df2666343c31cde69ecb2fbd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:40:52 +0000 Subject: [PATCH 270/408] fix: sanitize endpoint path params --- src/openai/_utils/__init__.py | 1 + src/openai/_utils/_path.py | 127 ++++++++++++++++++ src/openai/resources/batches.py | 10 +- src/openai/resources/beta/assistants.py | 14 +- src/openai/resources/beta/chatkit/sessions.py | 6 +- src/openai/resources/beta/chatkit/threads.py | 14 +- src/openai/resources/beta/threads/messages.py | 22 +-- .../resources/beta/threads/runs/runs.py | 37 ++--- .../resources/beta/threads/runs/steps.py | 20 ++- src/openai/resources/beta/threads/threads.py | 14 +- .../resources/chat/completions/completions.py | 14 +- .../resources/chat/completions/messages.py | 6 +- src/openai/resources/containers/containers.py | 10 +- .../resources/containers/files/content.py | 9 +- .../resources/containers/files/files.py | 18 +-- .../resources/conversations/conversations.py | 14 +- src/openai/resources/conversations/items.py | 26 ++-- src/openai/resources/evals/evals.py | 14 +- .../resources/evals/runs/output_items.py | 20 ++- src/openai/resources/evals/runs/runs.py | 22 +-- src/openai/resources/files.py | 18 +-- .../fine_tuning/checkpoints/permissions.py | 44 ++++-- .../resources/fine_tuning/jobs/checkpoints.py | 6 +- src/openai/resources/fine_tuning/jobs/jobs.py | 22 +-- src/openai/resources/models.py | 9 +- src/openai/resources/realtime/calls.py | 18 +-- src/openai/resources/responses/input_items.py | 6 +- src/openai/resources/responses/responses.py | 14 +- src/openai/resources/skills/content.py | 5 +- src/openai/resources/skills/skills.py | 14 +- .../resources/skills/versions/content.py | 5 +- .../resources/skills/versions/versions.py | 18 +-- src/openai/resources/uploads/parts.py | 6 +- src/openai/resources/uploads/uploads.py | 10 +- .../resources/vector_stores/file_batches.py | 42 ++++-- src/openai/resources/vector_stores/files.py | 46 +++++-- .../resources/vector_stores/vector_stores.py | 18 +-- src/openai/resources/videos.py | 22 +-- tests/test_utils/test_path.py | 89 ++++++++++++ 39 files changed, 577 insertions(+), 253 deletions(-) create mode 100644 src/openai/_utils/_path.py create mode 100644 tests/test_utils/test_path.py diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 963c83b6d4..52853aaf03 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -1,4 +1,5 @@ from ._logs import SensitiveHeadersFilter as SensitiveHeadersFilter +from ._path import path_template as path_template from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( diff --git a/src/openai/_utils/_path.py b/src/openai/_utils/_path.py new file mode 100644 index 0000000000..4d6e1e4cbc --- /dev/null +++ b/src/openai/_utils/_path.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from typing import ( + Any, + Mapping, + Callable, +) +from urllib.parse import quote + +# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). +_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") + +_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") + + +def _quote_path_segment_part(value: str) -> str: + """Percent-encode `value` for use in a URI path segment. + + Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 + """ + # quote() already treats unreserved characters (letters, digits, and -._~) + # as safe, so we only need to add sub-delims, ':', and '@'. + # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. + return quote(value, safe="!$&'()*+,;=:@") + + +def _quote_query_part(value: str) -> str: + """Percent-encode `value` for use in a URI query string. + + Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 + """ + return quote(value, safe="!$'()*+,;:@/?") + + +def _quote_fragment_part(value: str) -> str: + """Percent-encode `value` for use in a URI fragment. + + Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + """ + return quote(value, safe="!$&'()*+,;=:@/?") + + +def _interpolate( + template: str, + values: Mapping[str, Any], + quoter: Callable[[str], str], +) -> str: + """Replace {name} placeholders in `template`, quoting each value with `quoter`. + + Placeholder names are looked up in `values`. + + Raises: + KeyError: If a placeholder is not found in `values`. + """ + # re.split with a capturing group returns alternating + # [text, name, text, name, ..., text] elements. + parts = _PLACEHOLDER_RE.split(template) + + for i in range(1, len(parts), 2): + name = parts[i] + if name not in values: + raise KeyError(f"a value for placeholder {{{name}}} was not provided") + val = values[name] + if val is None: + parts[i] = "null" + elif isinstance(val, bool): + parts[i] = "true" if val else "false" + else: + parts[i] = quoter(str(values[name])) + + return "".join(parts) + + +def path_template(template: str, /, **kwargs: Any) -> str: + """Interpolate {name} placeholders in `template` from keyword arguments. + + Args: + template: The template string containing {name} placeholders. + **kwargs: Keyword arguments to interpolate into the template. + + Returns: + The template with placeholders interpolated and percent-encoded. + + Safe characters for percent-encoding are dependent on the URI component. + Placeholders in path and fragment portions are percent-encoded where the `segment` + and `fragment` sets from RFC 3986 respectively are considered safe. + Placeholders in the query portion are percent-encoded where the `query` set from + RFC 3986 §3.3 is considered safe except for = and & characters. + + Raises: + KeyError: If a placeholder is not found in `kwargs`. + ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). + """ + # Split the template into path, query, and fragment portions. + fragment_template: str | None = None + query_template: str | None = None + + rest = template + if "#" in rest: + rest, fragment_template = rest.split("#", 1) + if "?" in rest: + rest, query_template = rest.split("?", 1) + path_template = rest + + # Interpolate each portion with the appropriate quoting rules. + path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) + + # Reject dot-segments (. and ..) in the final assembled path. The check + # runs after interpolation so that adjacent placeholders or a mix of static + # text and placeholders that together form a dot-segment are caught. + # Also reject percent-encoded dot-segments to protect against incorrectly + # implemented normalization in servers/proxies. + for segment in path_result.split("/"): + if _DOT_SEGMENT_RE.match(segment): + raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") + + result = path_result + if query_template is not None: + result += "?" + _interpolate(query_template, kwargs, _quote_query_part) + if fragment_template is not None: + result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) + + return result diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index c269b93e2c..6cdb50c280 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -10,7 +10,7 @@ from .. import _legacy_response from ..types import batch_list_params, batch_create_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -154,7 +154,7 @@ def retrieve( if not batch_id: raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return self._get( - f"/batches/{batch_id}", + path_template("/batches/{batch_id}", batch_id=batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -242,7 +242,7 @@ def cancel( if not batch_id: raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return self._post( - f"/batches/{batch_id}/cancel", + path_template("/batches/{batch_id}/cancel", batch_id=batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -382,7 +382,7 @@ async def retrieve( if not batch_id: raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return await self._get( - f"/batches/{batch_id}", + path_template("/batches/{batch_id}", batch_id=batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -470,7 +470,7 @@ async def cancel( if not batch_id: raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") return await self._post( - f"/batches/{batch_id}/cancel", + path_template("/batches/{batch_id}/cancel", batch_id=batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index bf22122553..7ea8a918ca 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -10,7 +10,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -215,7 +215,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/assistants/{assistant_id}", + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -383,7 +383,7 @@ def update( raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/assistants/{assistant_id}", + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), body=maybe_transform( { "description": description, @@ -500,7 +500,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( - f"/assistants/{assistant_id}", + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -691,7 +691,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/assistants/{assistant_id}", + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -859,7 +859,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/assistants/{assistant_id}", + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), body=await async_maybe_transform( { "description": description, @@ -976,7 +976,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `assistant_id` but received {assistant_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( - f"/assistants/{assistant_id}", + path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/chatkit/sessions.py b/src/openai/resources/beta/chatkit/sessions.py index abfa496a56..6e95fd65fb 100644 --- a/src/openai/resources/beta/chatkit/sessions.py +++ b/src/openai/resources/beta/chatkit/sessions.py @@ -6,7 +6,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform, async_maybe_transform +from ...._utils import path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -134,7 +134,7 @@ def cancel( raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return self._post( - f"/chatkit/sessions/{session_id}/cancel", + path_template("/chatkit/sessions/{session_id}/cancel", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -249,7 +249,7 @@ async def cancel( raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return await self._post( - f"/chatkit/sessions/{session_id}/cancel", + path_template("/chatkit/sessions/{session_id}/cancel", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/chatkit/threads.py b/src/openai/resources/beta/chatkit/threads.py index 7a2d4c4a30..16e0e11a0a 100644 --- a/src/openai/resources/beta/chatkit/threads.py +++ b/src/openai/resources/beta/chatkit/threads.py @@ -9,7 +9,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform +from ...._utils import path_template, maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -70,7 +70,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return self._get( - f"/chatkit/threads/{thread_id}", + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -167,7 +167,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return self._delete( - f"/chatkit/threads/{thread_id}", + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -215,7 +215,7 @@ def list_items( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return self._get_api_list( - f"/chatkit/threads/{thread_id}/items", + path_template("/chatkit/threads/{thread_id}/items", thread_id=thread_id), page=SyncConversationCursorPage[Data], options=make_request_options( extra_headers=extra_headers, @@ -283,7 +283,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return await self._get( - f"/chatkit/threads/{thread_id}", + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -380,7 +380,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return await self._delete( - f"/chatkit/threads/{thread_id}", + path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -428,7 +428,7 @@ def list_items( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "chatkit_beta=v1", **(extra_headers or {})} return self._get_api_list( - f"/chatkit/threads/{thread_id}/items", + path_template("/chatkit/threads/{thread_id}/items", thread_id=thread_id), page=AsyncConversationCursorPage[Data], options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index e783310933..95b750d4e4 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -10,7 +10,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform, async_maybe_transform +from ...._utils import path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -101,7 +101,7 @@ def create( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/threads/{thread_id}/messages", + path_template("/threads/{thread_id}/messages", thread_id=thread_id), body=maybe_transform( { "content": content, @@ -148,7 +148,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/threads/{thread_id}/messages/{message_id}", + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -194,7 +194,7 @@ def update( raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/threads/{thread_id}/messages/{message_id}", + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -253,7 +253,7 @@ def list( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/threads/{thread_id}/messages", + path_template("/threads/{thread_id}/messages", thread_id=thread_id), page=SyncCursorPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -305,7 +305,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( - f"/threads/{thread_id}/messages/{message_id}", + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -386,7 +386,7 @@ async def create( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/threads/{thread_id}/messages", + path_template("/threads/{thread_id}/messages", thread_id=thread_id), body=await async_maybe_transform( { "content": content, @@ -433,7 +433,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/threads/{thread_id}/messages/{message_id}", + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -479,7 +479,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/threads/{thread_id}/messages/{message_id}", + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), body=await async_maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -538,7 +538,7 @@ def list( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/threads/{thread_id}/messages", + path_template("/threads/{thread_id}/messages", thread_id=thread_id), page=AsyncCursorPage[Message], options=make_request_options( extra_headers=extra_headers, @@ -590,7 +590,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( - f"/threads/{thread_id}/messages/{message_id}", + path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 20862185d2..882e88dfa6 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -21,6 +21,7 @@ from ....._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, omit, not_given from ....._utils import ( is_given, + path_template, required_args, maybe_transform, async_maybe_transform, @@ -594,7 +595,7 @@ def create( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), body=maybe_transform( { "assistant_id": assistant_id, @@ -661,7 +662,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/threads/{thread_id}/runs/{run_id}", + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -707,7 +708,7 @@ def update( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/threads/{thread_id}/runs/{run_id}", + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -763,7 +764,7 @@ def list( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), page=SyncCursorPage[Run], options=make_request_options( extra_headers=extra_headers, @@ -814,7 +815,7 @@ def cancel( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/threads/{thread_id}/runs/{run_id}/cancel", + path_template("/threads/{thread_id}/runs/{run_id}/cancel", thread_id=thread_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -998,7 +999,7 @@ def create_and_stream( } make_request = partial( self._post, - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), body=maybe_transform( { "assistant_id": assistant_id, @@ -1185,7 +1186,7 @@ def stream( } make_request = partial( self._post, - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), body=maybe_transform( { "assistant_id": assistant_id, @@ -1361,7 +1362,7 @@ def submit_tool_outputs( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), body=maybe_transform( { "tool_outputs": tool_outputs, @@ -1502,7 +1503,7 @@ def submit_tool_outputs_stream( } request = partial( self._post, - f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), body=maybe_transform( { "tool_outputs": tool_outputs, @@ -2057,7 +2058,7 @@ async def create( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), body=await async_maybe_transform( { "assistant_id": assistant_id, @@ -2124,7 +2125,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/threads/{thread_id}/runs/{run_id}", + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2170,7 +2171,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/threads/{thread_id}/runs/{run_id}", + path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), body=await async_maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -2226,7 +2227,7 @@ def list( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), page=AsyncCursorPage[Run], options=make_request_options( extra_headers=extra_headers, @@ -2277,7 +2278,7 @@ async def cancel( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/threads/{thread_id}/runs/{run_id}/cancel", + path_template("/threads/{thread_id}/runs/{run_id}/cancel", thread_id=thread_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2460,7 +2461,7 @@ def create_and_stream( **(extra_headers or {}), } request = self._post( - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), body=maybe_transform( { "assistant_id": assistant_id, @@ -2647,7 +2648,7 @@ def stream( **(extra_headers or {}), } request = self._post( - f"/threads/{thread_id}/runs", + path_template("/threads/{thread_id}/runs", thread_id=thread_id), body=maybe_transform( { "assistant_id": assistant_id, @@ -2823,7 +2824,7 @@ async def submit_tool_outputs( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), body=await async_maybe_transform( { "tool_outputs": tool_outputs, @@ -2966,7 +2967,7 @@ def submit_tool_outputs_stream( **(extra_headers or {}), } request = self._post( - f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", + path_template("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs", thread_id=thread_id, run_id=run_id), body=maybe_transform( { "tool_outputs": tool_outputs, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index dea5df69bc..9a6402b263 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -10,7 +10,7 @@ from ..... import _legacy_response from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ....._utils import maybe_transform, async_maybe_transform +from ....._utils import path_template, maybe_transform, async_maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -88,7 +88,12 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + path_template( + "/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + thread_id=thread_id, + run_id=run_id, + step_id=step_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -159,7 +164,7 @@ def list( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/threads/{thread_id}/runs/{run_id}/steps", + path_template("/threads/{thread_id}/runs/{run_id}/steps", thread_id=thread_id, run_id=run_id), page=SyncCursorPage[RunStep], options=make_request_options( extra_headers=extra_headers, @@ -246,7 +251,12 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `step_id` but received {step_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + path_template( + "/threads/{thread_id}/runs/{run_id}/steps/{step_id}", + thread_id=thread_id, + run_id=run_id, + step_id=step_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -317,7 +327,7 @@ def list( raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/threads/{thread_id}/runs/{run_id}/steps", + path_template("/threads/{thread_id}/runs/{run_id}/steps", thread_id=thread_id, run_id=run_id), page=AsyncCursorPage[RunStep], options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 0a93baf452..4b0f18fe47 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -19,7 +19,7 @@ AsyncMessagesWithStreamingResponse, ) from ...._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._utils import path_template, required_args, maybe_transform, async_maybe_transform from .runs.runs import ( Runs, AsyncRuns, @@ -177,7 +177,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/threads/{thread_id}", + path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -226,7 +226,7 @@ def update( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/threads/{thread_id}", + path_template("/threads/{thread_id}", thread_id=thread_id), body=maybe_transform( { "metadata": metadata, @@ -268,7 +268,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( - f"/threads/{thread_id}", + path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1043,7 +1043,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/threads/{thread_id}", + path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1092,7 +1092,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/threads/{thread_id}", + path_template("/threads/{thread_id}", thread_id=thread_id), body=await async_maybe_transform( { "metadata": metadata, @@ -1134,7 +1134,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( - f"/threads/{thread_id}", + path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index a705c1f658..845bd1a1e1 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -20,7 +20,7 @@ AsyncMessagesWithStreamingResponse, ) from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._utils import path_template, required_args, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -1288,7 +1288,7 @@ def retrieve( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return self._get( - f"/chat/completions/{completion_id}", + path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1332,7 +1332,7 @@ def update( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return self._post( - f"/chat/completions/{completion_id}", + path_template("/chat/completions/{completion_id}", completion_id=completion_id), body=maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -1433,7 +1433,7 @@ def delete( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return self._delete( - f"/chat/completions/{completion_id}", + path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2791,7 +2791,7 @@ async def retrieve( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return await self._get( - f"/chat/completions/{completion_id}", + path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2835,7 +2835,7 @@ async def update( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return await self._post( - f"/chat/completions/{completion_id}", + path_template("/chat/completions/{completion_id}", completion_id=completion_id), body=await async_maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -2936,7 +2936,7 @@ async def delete( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return await self._delete( - f"/chat/completions/{completion_id}", + path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py index b1c6a08d51..ffbff566db 100644 --- a/src/openai/resources/chat/completions/messages.py +++ b/src/openai/resources/chat/completions/messages.py @@ -8,7 +8,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform +from ...._utils import path_template, maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -82,7 +82,7 @@ def list( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return self._get_api_list( - f"/chat/completions/{completion_id}/messages", + path_template("/chat/completions/{completion_id}/messages", completion_id=completion_id), page=SyncCursorPage[ChatCompletionStoreMessage], options=make_request_options( extra_headers=extra_headers, @@ -164,7 +164,7 @@ def list( if not completion_id: raise ValueError(f"Expected a non-empty value for `completion_id` but received {completion_id!r}") return self._get_api_list( - f"/chat/completions/{completion_id}/messages", + path_template("/chat/completions/{completion_id}/messages", completion_id=completion_id), page=AsyncCursorPage[ChatCompletionStoreMessage], options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py index 216097d9c8..f6b8c33c75 100644 --- a/src/openai/resources/containers/containers.py +++ b/src/openai/resources/containers/containers.py @@ -10,7 +10,7 @@ from ... import _legacy_response from ...types import container_list_params, container_create_params from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -140,7 +140,7 @@ def retrieve( if not container_id: raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") return self._get( - f"/containers/{container_id}", + path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -235,7 +235,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/containers/{container_id}", + path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -352,7 +352,7 @@ async def retrieve( if not container_id: raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") return await self._get( - f"/containers/{container_id}", + path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -447,7 +447,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/containers/{container_id}", + path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/containers/files/content.py b/src/openai/resources/containers/files/content.py index a3dbd0e8c7..eb915b9c13 100644 --- a/src/openai/resources/containers/files/content.py +++ b/src/openai/resources/containers/files/content.py @@ -6,6 +6,7 @@ from .... import _legacy_response from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -69,7 +70,9 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return self._get( - f"/containers/{container_id}/files/{file_id}/content", + path_template( + "/containers/{container_id}/files/{file_id}/content", container_id=container_id, file_id=file_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -127,7 +130,9 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return await self._get( - f"/containers/{container_id}/files/{file_id}/content", + path_template( + "/containers/{container_id}/files/{file_id}/content", container_id=container_id, file_id=file_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py index 62659a5c3d..f48adf3a2a 100644 --- a/src/openai/resources/containers/files/files.py +++ b/src/openai/resources/containers/files/files.py @@ -17,7 +17,7 @@ AsyncContentWithStreamingResponse, ) from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given -from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ...._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -102,7 +102,7 @@ def create( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( - f"/containers/{container_id}/files", + path_template("/containers/{container_id}/files", container_id=container_id), body=maybe_transform(body, file_create_params.FileCreateParams), files=files, options=make_request_options( @@ -140,7 +140,7 @@ def retrieve( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._get( - f"/containers/{container_id}/files/{file_id}", + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -188,7 +188,7 @@ def list( if not container_id: raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") return self._get_api_list( - f"/containers/{container_id}/files", + path_template("/containers/{container_id}/files", container_id=container_id), page=SyncCursorPage[FileListResponse], options=make_request_options( extra_headers=extra_headers, @@ -237,7 +237,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/containers/{container_id}/files/{file_id}", + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -316,7 +316,7 @@ async def create( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( - f"/containers/{container_id}/files", + path_template("/containers/{container_id}/files", container_id=container_id), body=await async_maybe_transform(body, file_create_params.FileCreateParams), files=files, options=make_request_options( @@ -354,7 +354,7 @@ async def retrieve( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._get( - f"/containers/{container_id}/files/{file_id}", + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -402,7 +402,7 @@ def list( if not container_id: raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") return self._get_api_list( - f"/containers/{container_id}/files", + path_template("/containers/{container_id}/files", container_id=container_id), page=AsyncCursorPage[FileListResponse], options=make_request_options( extra_headers=extra_headers, @@ -451,7 +451,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/containers/{container_id}/files/{file_id}", + path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py index f2c54e4d04..d349f38546 100644 --- a/src/openai/resources/conversations/conversations.py +++ b/src/openai/resources/conversations/conversations.py @@ -16,7 +16,7 @@ AsyncItemsWithStreamingResponse, ) from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -132,7 +132,7 @@ def retrieve( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._get( - f"/conversations/{conversation_id}", + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -173,7 +173,7 @@ def update( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._post( - f"/conversations/{conversation_id}", + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), body=maybe_transform({"metadata": metadata}, conversation_update_params.ConversationUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -208,7 +208,7 @@ def delete( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._delete( - f"/conversations/{conversation_id}", + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -318,7 +318,7 @@ async def retrieve( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._get( - f"/conversations/{conversation_id}", + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -359,7 +359,7 @@ async def update( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._post( - f"/conversations/{conversation_id}", + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), body=await async_maybe_transform( {"metadata": metadata}, conversation_update_params.ConversationUpdateParams ), @@ -396,7 +396,7 @@ async def delete( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._delete( - f"/conversations/{conversation_id}", + path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py index 1f8e101f7f..7d7c9a4aba 100644 --- a/src/openai/resources/conversations/items.py +++ b/src/openai/resources/conversations/items.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -81,7 +81,7 @@ def create( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._post( - f"/conversations/{conversation_id}/items", + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), body=maybe_transform({"items": items}, item_create_params.ItemCreateParams), options=make_request_options( extra_headers=extra_headers, @@ -129,7 +129,9 @@ def retrieve( return cast( ConversationItem, self._get( - f"/conversations/{conversation_id}/items/{item_id}", + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -200,7 +202,7 @@ def list( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._get_api_list( - f"/conversations/{conversation_id}/items", + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), page=SyncConversationCursorPage[ConversationItem], options=make_request_options( extra_headers=extra_headers, @@ -249,7 +251,9 @@ def delete( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return self._delete( - f"/conversations/{conversation_id}/items/{item_id}", + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -313,7 +317,7 @@ async def create( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return await self._post( - f"/conversations/{conversation_id}/items", + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), body=await async_maybe_transform({"items": items}, item_create_params.ItemCreateParams), options=make_request_options( extra_headers=extra_headers, @@ -361,7 +365,9 @@ async def retrieve( return cast( ConversationItem, await self._get( - f"/conversations/{conversation_id}/items/{item_id}", + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -432,7 +438,7 @@ def list( if not conversation_id: raise ValueError(f"Expected a non-empty value for `conversation_id` but received {conversation_id!r}") return self._get_api_list( - f"/conversations/{conversation_id}/items", + path_template("/conversations/{conversation_id}/items", conversation_id=conversation_id), page=AsyncConversationCursorPage[ConversationItem], options=make_request_options( extra_headers=extra_headers, @@ -481,7 +487,9 @@ async def delete( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return await self._delete( - f"/conversations/{conversation_id}/items/{item_id}", + path_template( + "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py index f0fe28fe8c..6acd669a2c 100644 --- a/src/openai/resources/evals/evals.py +++ b/src/openai/resources/evals/evals.py @@ -10,7 +10,7 @@ from ... import _legacy_response from ...types import eval_list_params, eval_create_params, eval_update_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from .runs.runs import ( Runs, @@ -152,7 +152,7 @@ def retrieve( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return self._get( - f"/evals/{eval_id}", + path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -196,7 +196,7 @@ def update( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return self._post( - f"/evals/{eval_id}", + path_template("/evals/{eval_id}", eval_id=eval_id), body=maybe_transform( { "metadata": metadata, @@ -293,7 +293,7 @@ def delete( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return self._delete( - f"/evals/{eval_id}", + path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -419,7 +419,7 @@ async def retrieve( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return await self._get( - f"/evals/{eval_id}", + path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -463,7 +463,7 @@ async def update( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return await self._post( - f"/evals/{eval_id}", + path_template("/evals/{eval_id}", eval_id=eval_id), body=await async_maybe_transform( { "metadata": metadata, @@ -560,7 +560,7 @@ async def delete( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return await self._delete( - f"/evals/{eval_id}", + path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/evals/runs/output_items.py b/src/openai/resources/evals/runs/output_items.py index c2e6647715..7a498a7ebf 100644 --- a/src/openai/resources/evals/runs/output_items.py +++ b/src/openai/resources/evals/runs/output_items.py @@ -8,7 +8,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform +from ...._utils import path_template, maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -75,7 +75,12 @@ def retrieve( if not output_item_id: raise ValueError(f"Expected a non-empty value for `output_item_id` but received {output_item_id!r}") return self._get( - f"/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + path_template( + "/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + eval_id=eval_id, + run_id=run_id, + output_item_id=output_item_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -125,7 +130,7 @@ def list( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get_api_list( - f"/evals/{eval_id}/runs/{run_id}/output_items", + path_template("/evals/{eval_id}/runs/{run_id}/output_items", eval_id=eval_id, run_id=run_id), page=SyncCursorPage[OutputItemListResponse], options=make_request_options( extra_headers=extra_headers, @@ -200,7 +205,12 @@ async def retrieve( if not output_item_id: raise ValueError(f"Expected a non-empty value for `output_item_id` but received {output_item_id!r}") return await self._get( - f"/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + path_template( + "/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}", + eval_id=eval_id, + run_id=run_id, + output_item_id=output_item_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -250,7 +260,7 @@ def list( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get_api_list( - f"/evals/{eval_id}/runs/{run_id}/output_items", + path_template("/evals/{eval_id}/runs/{run_id}/output_items", eval_id=eval_id, run_id=run_id), page=AsyncCursorPage[OutputItemListResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py index 49eecd768f..152ce9cb77 100644 --- a/src/openai/resources/evals/runs/runs.py +++ b/src/openai/resources/evals/runs/runs.py @@ -9,7 +9,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform, async_maybe_transform +from ...._utils import path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -103,7 +103,7 @@ def create( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return self._post( - f"/evals/{eval_id}/runs", + path_template("/evals/{eval_id}/runs", eval_id=eval_id), body=maybe_transform( { "data_source": data_source, @@ -147,7 +147,7 @@ def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._get( - f"/evals/{eval_id}/runs/{run_id}", + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -194,7 +194,7 @@ def list( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return self._get_api_list( - f"/evals/{eval_id}/runs", + path_template("/evals/{eval_id}/runs", eval_id=eval_id), page=SyncCursorPage[RunListResponse], options=make_request_options( extra_headers=extra_headers, @@ -243,7 +243,7 @@ def delete( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._delete( - f"/evals/{eval_id}/runs/{run_id}", + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -279,7 +279,7 @@ def cancel( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return self._post( - f"/evals/{eval_id}/runs/{run_id}", + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -356,7 +356,7 @@ async def create( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return await self._post( - f"/evals/{eval_id}/runs", + path_template("/evals/{eval_id}/runs", eval_id=eval_id), body=await async_maybe_transform( { "data_source": data_source, @@ -400,7 +400,7 @@ async def retrieve( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return await self._get( - f"/evals/{eval_id}/runs/{run_id}", + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -447,7 +447,7 @@ def list( if not eval_id: raise ValueError(f"Expected a non-empty value for `eval_id` but received {eval_id!r}") return self._get_api_list( - f"/evals/{eval_id}/runs", + path_template("/evals/{eval_id}/runs", eval_id=eval_id), page=AsyncCursorPage[RunListResponse], options=make_request_options( extra_headers=extra_headers, @@ -496,7 +496,7 @@ async def delete( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return await self._delete( - f"/evals/{eval_id}/runs/{run_id}", + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -532,7 +532,7 @@ async def cancel( if not run_id: raise ValueError(f"Expected a non-empty value for `run_id` but received {run_id!r}") return await self._post( - f"/evals/{eval_id}/runs/{run_id}", + path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 7341b326dc..b03f11b06a 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -12,7 +12,7 @@ from .. import _legacy_response from ..types import FilePurpose, file_list_params, file_create_params from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -164,7 +164,7 @@ def retrieve( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._get( - f"/files/{file_id}", + path_template("/files/{file_id}", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -258,7 +258,7 @@ def delete( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._delete( - f"/files/{file_id}", + path_template("/files/{file_id}", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -292,7 +292,7 @@ def content( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return self._get( - f"/files/{file_id}/content", + path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -326,7 +326,7 @@ def retrieve_content( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return self._get( - f"/files/{file_id}/content", + path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -489,7 +489,7 @@ async def retrieve( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._get( - f"/files/{file_id}", + path_template("/files/{file_id}", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -583,7 +583,7 @@ async def delete( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._delete( - f"/files/{file_id}", + path_template("/files/{file_id}", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -617,7 +617,7 @@ async def content( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return await self._get( - f"/files/{file_id}/content", + path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -651,7 +651,7 @@ async def retrieve_content( if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") return await self._get( - f"/files/{file_id}/content", + path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py index 35e06feee0..15184e130b 100644 --- a/src/openai/resources/fine_tuning/checkpoints/permissions.py +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -9,7 +9,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ...._utils import maybe_transform, async_maybe_transform +from ...._utils import path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -84,7 +84,10 @@ def create( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) return self._get_api_list( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), page=SyncPage[PermissionCreateResponse], body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), options=make_request_options( @@ -138,7 +141,10 @@ def retrieve( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) return self._get( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -200,7 +206,10 @@ def list( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) return self._get_api_list( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), page=SyncConversationCursorPage[PermissionListResponse], options=make_request_options( extra_headers=extra_headers, @@ -254,7 +263,11 @@ def delete( if not permission_id: raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") return self._delete( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + permission_id=permission_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -318,7 +331,10 @@ def create( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) return self._get_api_list( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), page=AsyncPage[PermissionCreateResponse], body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), options=make_request_options( @@ -372,7 +388,10 @@ async def retrieve( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) return await self._get( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -434,7 +453,10 @@ def list( f"Expected a non-empty value for `fine_tuned_model_checkpoint` but received {fine_tuned_model_checkpoint!r}" ) return self._get_api_list( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + ), page=AsyncConversationCursorPage[PermissionListResponse], options=make_request_options( extra_headers=extra_headers, @@ -488,7 +510,11 @@ async def delete( if not permission_id: raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") return await self._delete( - f"/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", + path_template( + "/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}", + fine_tuned_model_checkpoint=fine_tuned_model_checkpoint, + permission_id=permission_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py index 6f14a0994e..0f91a6218a 100644 --- a/src/openai/resources/fine_tuning/jobs/checkpoints.py +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -6,7 +6,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform +from ...._utils import path_template, maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -75,7 +75,7 @@ def list( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get_api_list( - f"/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", fine_tuning_job_id=fine_tuning_job_id), page=SyncCursorPage[FineTuningJobCheckpoint], options=make_request_options( extra_headers=extra_headers, @@ -148,7 +148,7 @@ def list( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get_api_list( - f"/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints", fine_tuning_job_id=fine_tuning_job_id), page=AsyncCursorPage[FineTuningJobCheckpoint], options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index e38baa5539..a948b10349 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -9,7 +9,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform, async_maybe_transform +from ...._utils import path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from .checkpoints import ( Checkpoints, @@ -208,7 +208,7 @@ def retrieve( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get( - f"/fine_tuning/jobs/{fine_tuning_job_id}", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -293,7 +293,7 @@ def cancel( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._post( - f"/fine_tuning/jobs/{fine_tuning_job_id}/cancel", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/cancel", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -332,7 +332,7 @@ def list_events( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get_api_list( - f"/fine_tuning/jobs/{fine_tuning_job_id}/events", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/events", fine_tuning_job_id=fine_tuning_job_id), page=SyncCursorPage[FineTuningJobEvent], options=make_request_options( extra_headers=extra_headers, @@ -376,7 +376,7 @@ def pause( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._post( - f"/fine_tuning/jobs/{fine_tuning_job_id}/pause", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/pause", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -409,7 +409,7 @@ def resume( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._post( - f"/fine_tuning/jobs/{fine_tuning_job_id}/resume", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/resume", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -591,7 +591,7 @@ async def retrieve( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return await self._get( - f"/fine_tuning/jobs/{fine_tuning_job_id}", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -676,7 +676,7 @@ async def cancel( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return await self._post( - f"/fine_tuning/jobs/{fine_tuning_job_id}/cancel", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/cancel", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -715,7 +715,7 @@ def list_events( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return self._get_api_list( - f"/fine_tuning/jobs/{fine_tuning_job_id}/events", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/events", fine_tuning_job_id=fine_tuning_job_id), page=AsyncCursorPage[FineTuningJobEvent], options=make_request_options( extra_headers=extra_headers, @@ -759,7 +759,7 @@ async def pause( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return await self._post( - f"/fine_tuning/jobs/{fine_tuning_job_id}/pause", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/pause", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -792,7 +792,7 @@ async def resume( if not fine_tuning_job_id: raise ValueError(f"Expected a non-empty value for `fine_tuning_job_id` but received {fine_tuning_job_id!r}") return await self._post( - f"/fine_tuning/jobs/{fine_tuning_job_id}/resume", + path_template("/fine_tuning/jobs/{fine_tuning_job_id}/resume", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index 508393263f..a1fe0d395e 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -6,6 +6,7 @@ from .. import _legacy_response from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import path_template from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -69,7 +70,7 @@ def retrieve( if not model: raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return self._get( - f"/models/{model}", + path_template("/models/{model}", model=model), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -127,7 +128,7 @@ def delete( if not model: raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return self._delete( - f"/models/{model}", + path_template("/models/{model}", model=model), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -184,7 +185,7 @@ async def retrieve( if not model: raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return await self._get( - f"/models/{model}", + path_template("/models/{model}", model=model), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -242,7 +243,7 @@ async def delete( if not model: raise ValueError(f"Expected a non-empty value for `model` but received {model!r}") return await self._delete( - f"/models/{model}", + path_template("/models/{model}", model=model), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index 1520a97b0a..f34748d239 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -230,7 +230,7 @@ def accept( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( - f"/realtime/calls/{call_id}/accept", + path_template("/realtime/calls/{call_id}/accept", call_id=call_id), body=maybe_transform( { "type": type, @@ -281,7 +281,7 @@ def hangup( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( - f"/realtime/calls/{call_id}/hangup", + path_template("/realtime/calls/{call_id}/hangup", call_id=call_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -319,7 +319,7 @@ def refer( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( - f"/realtime/calls/{call_id}/refer", + path_template("/realtime/calls/{call_id}/refer", call_id=call_id), body=maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -358,7 +358,7 @@ def reject( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( - f"/realtime/calls/{call_id}/reject", + path_template("/realtime/calls/{call_id}/reject", call_id=call_id), body=maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -559,7 +559,7 @@ async def accept( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( - f"/realtime/calls/{call_id}/accept", + path_template("/realtime/calls/{call_id}/accept", call_id=call_id), body=await async_maybe_transform( { "type": type, @@ -610,7 +610,7 @@ async def hangup( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( - f"/realtime/calls/{call_id}/hangup", + path_template("/realtime/calls/{call_id}/hangup", call_id=call_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -648,7 +648,7 @@ async def refer( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( - f"/realtime/calls/{call_id}/refer", + path_template("/realtime/calls/{call_id}/refer", call_id=call_id), body=await async_maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -687,7 +687,7 @@ async def reject( raise ValueError(f"Expected a non-empty value for `call_id` but received {call_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( - f"/realtime/calls/{call_id}/reject", + path_template("/realtime/calls/{call_id}/reject", call_id=call_id), body=await async_maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py index 3311bfe10a..b9ae5eeeae 100644 --- a/src/openai/resources/responses/input_items.py +++ b/src/openai/resources/responses/input_items.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -85,7 +85,7 @@ def list( if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._get_api_list( - f"/responses/{response_id}/input_items", + path_template("/responses/{response_id}/input_items", response_id=response_id), page=SyncCursorPage[ResponseItem], options=make_request_options( extra_headers=extra_headers, @@ -169,7 +169,7 @@ def list( if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._get_api_list( - f"/responses/{response_id}/input_items", + path_template("/responses/{response_id}/input_items", response_id=response_id), page=AsyncCursorPage[ResponseItem], options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 6d57f86313..63795f95a9 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -15,7 +15,7 @@ from ... import _legacy_response from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from ..._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform +from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._models import construct_type_unchecked from ..._resource import SyncAPIResource, AsyncAPIResource @@ -1471,7 +1471,7 @@ def retrieve( if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._get( - f"/responses/{response_id}", + path_template("/responses/{response_id}", response_id=response_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1519,7 +1519,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/responses/{response_id}", + path_template("/responses/{response_id}", response_id=response_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1555,7 +1555,7 @@ def cancel( if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return self._post( - f"/responses/{response_id}/cancel", + path_template("/responses/{response_id}/cancel", response_id=response_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -3142,7 +3142,7 @@ async def retrieve( if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return await self._get( - f"/responses/{response_id}", + path_template("/responses/{response_id}", response_id=response_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -3190,7 +3190,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/responses/{response_id}", + path_template("/responses/{response_id}", response_id=response_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -3226,7 +3226,7 @@ async def cancel( if not response_id: raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") return await self._post( - f"/responses/{response_id}/cancel", + path_template("/responses/{response_id}/cancel", response_id=response_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/skills/content.py b/src/openai/resources/skills/content.py index c912fd3eb3..96b237177e 100644 --- a/src/openai/resources/skills/content.py +++ b/src/openai/resources/skills/content.py @@ -6,6 +6,7 @@ from ... import _legacy_response from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -66,7 +67,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return self._get( - f"/skills/{skill_id}/content", + path_template("/skills/{skill_id}/content", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -121,7 +122,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return await self._get( - f"/skills/{skill_id}/content", + path_template("/skills/{skill_id}/content", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/skills/skills.py b/src/openai/resources/skills/skills.py index 77bed029df..f44fb24607 100644 --- a/src/openai/resources/skills/skills.py +++ b/src/openai/resources/skills/skills.py @@ -28,7 +28,7 @@ omit, not_given, ) -from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -144,7 +144,7 @@ def retrieve( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return self._get( - f"/skills/{skill_id}", + path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -180,7 +180,7 @@ def update( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return self._post( - f"/skills/{skill_id}", + path_template("/skills/{skill_id}", skill_id=skill_id), body=maybe_transform({"default_version": default_version}, skill_update_params.SkillUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -266,7 +266,7 @@ def delete( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return self._delete( - f"/skills/{skill_id}", + path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -370,7 +370,7 @@ async def retrieve( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return await self._get( - f"/skills/{skill_id}", + path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -406,7 +406,7 @@ async def update( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return await self._post( - f"/skills/{skill_id}", + path_template("/skills/{skill_id}", skill_id=skill_id), body=await async_maybe_transform( {"default_version": default_version}, skill_update_params.SkillUpdateParams ), @@ -494,7 +494,7 @@ async def delete( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return await self._delete( - f"/skills/{skill_id}", + path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/skills/versions/content.py b/src/openai/resources/skills/versions/content.py index 182a563dde..2f54586718 100644 --- a/src/openai/resources/skills/versions/content.py +++ b/src/openai/resources/skills/versions/content.py @@ -6,6 +6,7 @@ from .... import _legacy_response from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import path_template from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -71,7 +72,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return self._get( - f"/skills/{skill_id}/versions/{version}/content", + path_template("/skills/{skill_id}/versions/{version}/content", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -131,7 +132,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return await self._get( - f"/skills/{skill_id}/versions/{version}/content", + path_template("/skills/{skill_id}/versions/{version}/content", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/skills/versions/versions.py b/src/openai/resources/skills/versions/versions.py index 610a24240a..8b48075cc3 100644 --- a/src/openai/resources/skills/versions/versions.py +++ b/src/openai/resources/skills/versions/versions.py @@ -27,7 +27,7 @@ omit, not_given, ) -from ...._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ...._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -108,7 +108,7 @@ def create( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( - f"/skills/{skill_id}/versions", + path_template("/skills/{skill_id}/versions", skill_id=skill_id), body=maybe_transform(body, version_create_params.VersionCreateParams), files=extracted_files, options=make_request_options( @@ -148,7 +148,7 @@ def retrieve( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return self._get( - f"/skills/{skill_id}/versions/{version}", + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -190,7 +190,7 @@ def list( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return self._get_api_list( - f"/skills/{skill_id}/versions", + path_template("/skills/{skill_id}/versions", skill_id=skill_id), page=SyncCursorPage[SkillVersion], options=make_request_options( extra_headers=extra_headers, @@ -240,7 +240,7 @@ def delete( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return self._delete( - f"/skills/{skill_id}/versions/{version}", + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -316,7 +316,7 @@ async def create( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( - f"/skills/{skill_id}/versions", + path_template("/skills/{skill_id}/versions", skill_id=skill_id), body=await async_maybe_transform(body, version_create_params.VersionCreateParams), files=extracted_files, options=make_request_options( @@ -356,7 +356,7 @@ async def retrieve( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return await self._get( - f"/skills/{skill_id}/versions/{version}", + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -398,7 +398,7 @@ def list( if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") return self._get_api_list( - f"/skills/{skill_id}/versions", + path_template("/skills/{skill_id}/versions", skill_id=skill_id), page=AsyncCursorPage[SkillVersion], options=make_request_options( extra_headers=extra_headers, @@ -448,7 +448,7 @@ async def delete( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return await self._delete( - f"/skills/{skill_id}/versions/{version}", + path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index 034547f308..cf09eea75e 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -86,7 +86,7 @@ def create( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return self._post( - f"/uploads/{upload_id}/parts", + path_template("/uploads/{upload_id}/parts", upload_id=upload_id), body=maybe_transform(body, part_create_params.PartCreateParams), files=files, options=make_request_options( @@ -163,7 +163,7 @@ async def create( # multipart/form-data; boundary=---abc-- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})} return await self._post( - f"/uploads/{upload_id}/parts", + path_template("/uploads/{upload_id}/parts", upload_id=upload_id), body=await async_maybe_transform(body, part_create_params.PartCreateParams), files=files, options=make_request_options( diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index f5e5e6f664..7778e51539 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -23,7 +23,7 @@ ) from ...types import FilePurpose, upload_create_params, upload_complete_params from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -276,7 +276,7 @@ def cancel( if not upload_id: raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") return self._post( - f"/uploads/{upload_id}/cancel", + path_template("/uploads/{upload_id}/cancel", upload_id=upload_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -330,7 +330,7 @@ def complete( if not upload_id: raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") return self._post( - f"/uploads/{upload_id}/complete", + path_template("/uploads/{upload_id}/complete", upload_id=upload_id), body=maybe_transform( { "part_ids": part_ids, @@ -592,7 +592,7 @@ async def cancel( if not upload_id: raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") return await self._post( - f"/uploads/{upload_id}/cancel", + path_template("/uploads/{upload_id}/cancel", upload_id=upload_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -646,7 +646,7 @@ async def complete( if not upload_id: raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") return await self._post( - f"/uploads/{upload_id}/complete", + path_template("/uploads/{upload_id}/complete", upload_id=upload_id), body=await async_maybe_transform( { "part_ids": part_ids, diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index 13ffa66d1a..f097cf8a92 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -13,7 +13,7 @@ from ... import _legacy_response from ...types import FileChunkingStrategyParam from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given -from ..._utils import is_given, maybe_transform, async_maybe_transform +from ..._utils import is_given, path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -100,7 +100,7 @@ def create( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/vector_stores/{vector_store_id}/file_batches", + path_template("/vector_stores/{vector_store_id}/file_batches", vector_store_id=vector_store_id), body=maybe_transform( { "attributes": attributes, @@ -146,7 +146,11 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/vector_stores/{vector_store_id}/file_batches/{batch_id}", + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -185,7 +189,11 @@ def cancel( raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -280,7 +288,11 @@ def list_files( raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), page=SyncCursorPage[VectorStoreFile], options=make_request_options( extra_headers=extra_headers, @@ -461,7 +473,7 @@ async def create( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/vector_stores/{vector_store_id}/file_batches", + path_template("/vector_stores/{vector_store_id}/file_batches", vector_store_id=vector_store_id), body=await async_maybe_transform( { "attributes": attributes, @@ -507,7 +519,11 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/vector_stores/{vector_store_id}/file_batches/{batch_id}", + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -546,7 +562,11 @@ async def cancel( raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -641,7 +661,11 @@ def list_files( raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + path_template( + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/files", + vector_store_id=vector_store_id, + batch_id=batch_id, + ), page=AsyncCursorPage[VectorStoreFile], options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py index 29f6e879f1..8666434587 100644 --- a/src/openai/resources/vector_stores/files.py +++ b/src/openai/resources/vector_stores/files.py @@ -10,7 +10,7 @@ from ... import _legacy_response from ...types import FileChunkingStrategyParam from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from ..._utils import is_given, maybe_transform, async_maybe_transform +from ..._utils import is_given, path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -90,7 +90,7 @@ def create( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/vector_stores/{vector_store_id}/files", + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), body=maybe_transform( { "file_id": file_id, @@ -135,7 +135,9 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/vector_stores/{vector_store_id}/files/{file_id}", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -179,7 +181,9 @@ def update( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/vector_stores/{vector_store_id}/files/{file_id}", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), body=maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -237,7 +241,7 @@ def list( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/files", + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), page=SyncCursorPage[VectorStoreFile], options=make_request_options( extra_headers=extra_headers, @@ -292,7 +296,9 @@ def delete( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( - f"/vector_stores/{vector_store_id}/files/{file_id}", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -437,7 +443,11 @@ def content( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/files/{file_id}/content", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}/content", + vector_store_id=vector_store_id, + file_id=file_id, + ), page=SyncPage[FileContentResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -511,7 +521,7 @@ async def create( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/vector_stores/{vector_store_id}/files", + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), body=await async_maybe_transform( { "file_id": file_id, @@ -556,7 +566,9 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/vector_stores/{vector_store_id}/files/{file_id}", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -600,7 +612,9 @@ async def update( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/vector_stores/{vector_store_id}/files/{file_id}", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), body=await async_maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -658,7 +672,7 @@ def list( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/files", + path_template("/vector_stores/{vector_store_id}/files", vector_store_id=vector_store_id), page=AsyncCursorPage[VectorStoreFile], options=make_request_options( extra_headers=extra_headers, @@ -713,7 +727,9 @@ async def delete( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( - f"/vector_stores/{vector_store_id}/files/{file_id}", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -860,7 +876,11 @@ def content( raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/files/{file_id}/content", + path_template( + "/vector_stores/{vector_store_id}/files/{file_id}/content", + vector_store_id=vector_store_id, + file_id=file_id, + ), page=AsyncPage[FileContentResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py index 490e3e7fdb..7fa2ad5274 100644 --- a/src/openai/resources/vector_stores/vector_stores.py +++ b/src/openai/resources/vector_stores/vector_stores.py @@ -24,7 +24,7 @@ vector_store_update_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -171,7 +171,7 @@ def retrieve( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get( - f"/vector_stores/{vector_store_id}", + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -219,7 +219,7 @@ def update( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._post( - f"/vector_stores/{vector_store_id}", + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), body=maybe_transform( { "expires_after": expires_after, @@ -326,7 +326,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._delete( - f"/vector_stores/{vector_store_id}", + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -377,7 +377,7 @@ def search( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/search", + path_template("/vector_stores/{vector_store_id}/search", vector_store_id=vector_store_id), page=SyncPage[VectorStoreSearchResponse], body=maybe_transform( { @@ -521,7 +521,7 @@ async def retrieve( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._get( - f"/vector_stores/{vector_store_id}", + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -569,7 +569,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._post( - f"/vector_stores/{vector_store_id}", + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), body=await async_maybe_transform( { "expires_after": expires_after, @@ -676,7 +676,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return await self._delete( - f"/vector_stores/{vector_store_id}", + path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -727,7 +727,7 @@ def search( raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}") extra_headers = {"OpenAI-Beta": "assistants=v2", **(extra_headers or {})} return self._get_api_list( - f"/vector_stores/{vector_store_id}/search", + path_template("/vector_stores/{vector_store_id}/search", vector_store_id=vector_store_id), page=AsyncPage[VectorStoreSearchResponse], body=maybe_transform( { diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index f387f55824..a006e64705 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -20,7 +20,7 @@ video_download_content_params, ) from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from .._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -227,7 +227,7 @@ def retrieve( if not video_id: raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") return self._get( - f"/videos/{video_id}", + path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -312,7 +312,7 @@ def delete( if not video_id: raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") return self._delete( - f"/videos/{video_id}", + path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -400,7 +400,7 @@ def download_content( raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return self._get( - f"/videos/{video_id}/content", + path_template("/videos/{video_id}/content", video_id=video_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -541,7 +541,7 @@ def get_character( if not character_id: raise ValueError(f"Expected a non-empty value for `character_id` but received {character_id!r}") return self._get( - f"/videos/characters/{character_id}", + path_template("/videos/characters/{character_id}", character_id=character_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -577,7 +577,7 @@ def remix( if not video_id: raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") return self._post( - f"/videos/{video_id}/remix", + path_template("/videos/{video_id}/remix", video_id=video_id), body=maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -768,7 +768,7 @@ async def retrieve( if not video_id: raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") return await self._get( - f"/videos/{video_id}", + path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -853,7 +853,7 @@ async def delete( if not video_id: raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") return await self._delete( - f"/videos/{video_id}", + path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -941,7 +941,7 @@ async def download_content( raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return await self._get( - f"/videos/{video_id}/content", + path_template("/videos/{video_id}/content", video_id=video_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1084,7 +1084,7 @@ async def get_character( if not character_id: raise ValueError(f"Expected a non-empty value for `character_id` but received {character_id!r}") return await self._get( - f"/videos/characters/{character_id}", + path_template("/videos/characters/{character_id}", character_id=character_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1120,7 +1120,7 @@ async def remix( if not video_id: raise ValueError(f"Expected a non-empty value for `video_id` but received {video_id!r}") return await self._post( - f"/videos/{video_id}/remix", + path_template("/videos/{video_id}/remix", video_id=video_id), body=await async_maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py new file mode 100644 index 0000000000..420cd19973 --- /dev/null +++ b/tests/test_utils/test_path.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from openai._utils._path import path_template + + +@pytest.mark.parametrize( + "template, kwargs, expected", + [ + ("/v1/{id}", dict(id="abc"), "/v1/abc"), + ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), + ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), + ("/{w}/{w}", dict(w="echo"), "/echo/echo"), + ("/v1/static", {}, "/v1/static"), + ("", {}, ""), + ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), + ("/v1/{v}", dict(v=None), "/v1/null"), + ("/v1/{v}", dict(v=True), "/v1/true"), + ("/v1/{v}", dict(v=False), "/v1/false"), + ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok + ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok + ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok + ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok + ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine + ( + "/v1/{a}?query={b}", + dict(a="../../other/endpoint", b="a&bad=true"), + "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", + ), + ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), + ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), + ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), + ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input + # Query: slash and ? are safe, # is not + ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), + ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), + ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), + ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), + # Fragment: slash and ? are safe + ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), + ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), + # Path: slash, ? and # are all encoded + ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), + ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), + ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), + # same var encoded differently by component + ( + "/v1/{v}?q={v}#{v}", + dict(v="a/b?c#d"), + "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", + ), + ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection + ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection + ], +) +def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: + assert path_template(template, **kwargs) == expected + + +def test_missing_kwarg_raises_key_error() -> None: + with pytest.raises(KeyError, match="org_id"): + path_template("/v1/{org_id}") + + +@pytest.mark.parametrize( + "template, kwargs", + [ + ("{a}/path", dict(a=".")), + ("{a}/path", dict(a="..")), + ("/v1/{a}", dict(a=".")), + ("/v1/{a}", dict(a="..")), + ("/v1/{a}/path", dict(a=".")), + ("/v1/{a}/path", dict(a="..")), + ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." + ("/v1/{a}.", dict(a=".")), # var + static → ".." + ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." + ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text + ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/{v}?q=1", dict(v="..")), + ("/v1/{v}#frag", dict(v="..")), + ], +) +def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: + with pytest.raises(ValueError, match="dot-segment"): + path_template(template, **kwargs) From 85ac93132025f39452c1cd7a17695803c9fc5867 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:59:11 +0000 Subject: [PATCH 271/408] refactor(tests): switch from prism to steady --- CONTRIBUTING.md | 2 +- scripts/mock | 26 +++++++++++++------------- scripts/test | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a1cf70bb8..253b9ce5e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,7 @@ $ pip install ./path-to-wheel-file.whl ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh $ ./scripts/mock diff --git a/scripts/mock b/scripts/mock index bcf3b392b3..3d1d19c19f 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,34 +19,34 @@ fi echo "==> Starting mock server with URL ${URL}" -# Run prism mock on the given spec +# Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stdy/cli@0.19.3 -- steady --version - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & - # Wait for server to come online (max 30s) + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" attempts=0 - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do + if ! kill -0 $! 2>/dev/null; then + echo + cat .stdy.log + exit 1 + fi attempts=$((attempts + 1)) if [ "$attempts" -ge 300 ]; then echo - echo "Timed out waiting for Prism server to start" - cat .prism.log + echo "Timed out waiting for Steady server to start" + cat .stdy.log exit 1 fi echo -n "." sleep 0.1 done - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - echo else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index dbeda2d217..d4fac354ef 100755 --- a/scripts/test +++ b/scripts/test @@ -9,8 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 +function steady_is_running() { + curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { @@ -25,7 +25,7 @@ function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } -if ! is_overriding_api_base_url && ! prism_is_running ; then +if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT @@ -36,19 +36,19 @@ fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" +elif ! steady_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" + echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi From c8c9508899b2119cc69e006403d09cbad7f616e4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 17:50:22 +0000 Subject: [PATCH 272/408] chore(tests): bump steady to v0.19.4 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 3d1d19c19f..e2ca85a09f 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.3 -- steady --version + npm exec --package=@stdy/cli@0.19.4 -- steady --version - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index d4fac354ef..4d15877e75 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 634b74edd4aaa07a74f9ee30241410d61624264f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:32:14 +0000 Subject: [PATCH 273/408] chore(tests): bump steady to v0.19.5 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index e2ca85a09f..4f7dfd12b4 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.4 -- steady --version + npm exec --package=@stdy/cli@0.19.5 -- steady --version - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 4d15877e75..3861edc6d1 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 78c764bdf483a0c48789bfdefe6299830d5abde0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:39:34 +0000 Subject: [PATCH 274/408] chore(internal): update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 55c6ca861f..3a7a575390 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log _dev __pycache__ From 56ad9ca089394e535d7df52fe48d544e54086ddc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:54:49 +0000 Subject: [PATCH 275/408] fix(types): make type required in ResponseInputMessageItem --- .stats.yml | 4 ++-- src/openai/types/responses/response_input_message_item.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index b47f21a4ac..2cf8eb516d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-55ef7034334e938c30656a404ce5e21466103be87542a796425346299f450404.yml -openapi_spec_hash: 4a5bfd2ee4ad47f5b7cf6f1ad08d5d7f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0fea07225431c8d0cf5fc1a70c9363a91d259f7a169f410717e162de1b24e489.yml +openapi_spec_hash: 41b34c1678ec0e95daf62ca4cd52c8f8 config_hash: 96fbf82cf74a44ccd513f5acf0956ffd diff --git a/src/openai/types/responses/response_input_message_item.py b/src/openai/types/responses/response_input_message_item.py index 6a788e7fa4..788c92c9bd 100644 --- a/src/openai/types/responses/response_input_message_item.py +++ b/src/openai/types/responses/response_input_message_item.py @@ -22,12 +22,12 @@ class ResponseInputMessageItem(BaseModel): role: Literal["user", "system", "developer"] """The role of the message input. One of `user`, `system`, or `developer`.""" + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None """The status of item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API. """ - - type: Optional[Literal["message"]] = None - """The type of the message input. Always set to `message`.""" From e3c59bf1ac8533a1be831a6d166f9f7abeabf8e0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:03:55 +0000 Subject: [PATCH 276/408] chore(tests): bump steady to v0.19.6 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 4f7dfd12b4..dba3058989 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.5 -- steady --version + npm exec --package=@stdy/cli@0.19.6 -- steady --version - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 3861edc6d1..004577df10 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 23bc02703bbb9497eadd5d56497d5d6954372a62 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:16:40 +0000 Subject: [PATCH 277/408] chore(ci): skip lint on metadata-only changes Note that we still want to run tests, as these depend on the metadata. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ad9f39206..b786cbcf6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 @@ -38,7 +38,7 @@ jobs: run: ./scripts/lint build: - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') timeout-minutes: 10 name: build permissions: @@ -107,7 +107,7 @@ jobs: timeout-minutes: 10 name: examples runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) + if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 From 4f43fe371037415ace13981a277917366b6fc24e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:44:12 +0000 Subject: [PATCH 278/408] chore(tests): bump steady to v0.19.7 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index dba3058989..9ecceca0a7 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.6 -- steady --version + npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 004577df10..9231513853 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From cd72fba37866bfdddd4a84420afe2ff397279582 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:19:13 +0000 Subject: [PATCH 279/408] feat(api): add keys field to Click/DoubleClick/Drag/Move/Scroll computer actions --- .stats.yml | 4 ++-- src/openai/types/responses/computer_action.py | 17 ++++++++++++++++- .../responses/computer_action_list_param.py | 17 ++++++++++++++++- .../types/responses/computer_action_param.py | 17 ++++++++++++++++- .../responses/response_computer_tool_call.py | 15 +++++++++++++++ .../response_computer_tool_call_param.py | 15 +++++++++++++++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2cf8eb516d..297349aa88 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-0fea07225431c8d0cf5fc1a70c9363a91d259f7a169f410717e162de1b24e489.yml -openapi_spec_hash: 41b34c1678ec0e95daf62ca4cd52c8f8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-13599f99dceef322e19171dcc63d90f638d225a445999442249e1ed7a4924c43.yml +openapi_spec_hash: aac8cf8ec3c7dc6d14ecf5dbb289ee7c config_hash: 96fbf82cf74a44ccd513f5acf0956ffd diff --git a/src/openai/types/responses/computer_action.py b/src/openai/types/responses/computer_action.py index a0c11084ba..f7a21d2ac4 100644 --- a/src/openai/types/responses/computer_action.py +++ b/src/openai/types/responses/computer_action.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union +from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -39,10 +39,16 @@ class Click(BaseModel): y: int """The y-coordinate where the click occurred.""" + keys: Optional[List[str]] = None + """The keys being held while clicking.""" + class DoubleClick(BaseModel): """A double click action.""" + keys: Optional[List[str]] = None + """The keys being held while double-clicking.""" + type: Literal["double_click"] """Specifies the event type. @@ -88,6 +94,9 @@ class Drag(BaseModel): For a drag action, this property is always set to `drag`. """ + keys: Optional[List[str]] = None + """The keys being held while dragging the mouse.""" + class Keypress(BaseModel): """A collection of keypresses the model would like to perform.""" @@ -120,6 +129,9 @@ class Move(BaseModel): y: int """The y-coordinate to move to.""" + keys: Optional[List[str]] = None + """The keys being held while moving the mouse.""" + class Screenshot(BaseModel): """A screenshot action.""" @@ -152,6 +164,9 @@ class Scroll(BaseModel): y: int """The y-coordinate where the scroll occurred.""" + keys: Optional[List[str]] = None + """The keys being held while scrolling.""" + class Type(BaseModel): """An action to type in text.""" diff --git a/src/openai/types/responses/computer_action_list_param.py b/src/openai/types/responses/computer_action_list_param.py index ec609ffd1b..66a03520f7 100644 --- a/src/openai/types/responses/computer_action_list_param.py +++ b/src/openai/types/responses/computer_action_list_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import List, Union, Iterable +from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr @@ -41,10 +41,16 @@ class Click(TypedDict, total=False): y: Required[int] """The y-coordinate where the click occurred.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + class DoubleClick(TypedDict, total=False): """A double click action.""" + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + type: Required[Literal["double_click"]] """Specifies the event type. @@ -90,6 +96,9 @@ class Drag(TypedDict, total=False): For a drag action, this property is always set to `drag`. """ + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + class Keypress(TypedDict, total=False): """A collection of keypresses the model would like to perform.""" @@ -122,6 +131,9 @@ class Move(TypedDict, total=False): y: Required[int] """The y-coordinate to move to.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + class Screenshot(TypedDict, total=False): """A screenshot action.""" @@ -154,6 +166,9 @@ class Scroll(TypedDict, total=False): y: Required[int] """The y-coordinate where the scroll occurred.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + class Type(TypedDict, total=False): """An action to type in text.""" diff --git a/src/openai/types/responses/computer_action_param.py b/src/openai/types/responses/computer_action_param.py index 822a77d759..f60c72b1b4 100644 --- a/src/openai/types/responses/computer_action_param.py +++ b/src/openai/types/responses/computer_action_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr @@ -40,10 +40,16 @@ class Click(TypedDict, total=False): y: Required[int] """The y-coordinate where the click occurred.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + class DoubleClick(TypedDict, total=False): """A double click action.""" + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + type: Required[Literal["double_click"]] """Specifies the event type. @@ -89,6 +95,9 @@ class Drag(TypedDict, total=False): For a drag action, this property is always set to `drag`. """ + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + class Keypress(TypedDict, total=False): """A collection of keypresses the model would like to perform.""" @@ -121,6 +130,9 @@ class Move(TypedDict, total=False): y: Required[int] """The y-coordinate to move to.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + class Screenshot(TypedDict, total=False): """A screenshot action.""" @@ -153,6 +165,9 @@ class Scroll(TypedDict, total=False): y: Required[int] """The y-coordinate where the scroll occurred.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + class Type(TypedDict, total=False): """An action to type in text.""" diff --git a/src/openai/types/responses/response_computer_tool_call.py b/src/openai/types/responses/response_computer_tool_call.py index f796846560..e0339e4afd 100644 --- a/src/openai/types/responses/response_computer_tool_call.py +++ b/src/openai/types/responses/response_computer_tool_call.py @@ -55,10 +55,16 @@ class ActionClick(BaseModel): y: int """The y-coordinate where the click occurred.""" + keys: Optional[List[str]] = None + """The keys being held while clicking.""" + class ActionDoubleClick(BaseModel): """A double click action.""" + keys: Optional[List[str]] = None + """The keys being held while double-clicking.""" + type: Literal["double_click"] """Specifies the event type. @@ -104,6 +110,9 @@ class ActionDrag(BaseModel): For a drag action, this property is always set to `drag`. """ + keys: Optional[List[str]] = None + """The keys being held while dragging the mouse.""" + class ActionKeypress(BaseModel): """A collection of keypresses the model would like to perform.""" @@ -136,6 +145,9 @@ class ActionMove(BaseModel): y: int """The y-coordinate to move to.""" + keys: Optional[List[str]] = None + """The keys being held while moving the mouse.""" + class ActionScreenshot(BaseModel): """A screenshot action.""" @@ -168,6 +180,9 @@ class ActionScroll(BaseModel): y: int """The y-coordinate where the scroll occurred.""" + keys: Optional[List[str]] = None + """The keys being held while scrolling.""" + class ActionType(BaseModel): """An action to type in text.""" diff --git a/src/openai/types/responses/response_computer_tool_call_param.py b/src/openai/types/responses/response_computer_tool_call_param.py index 05cc2c2f67..3c121097e5 100644 --- a/src/openai/types/responses/response_computer_tool_call_param.py +++ b/src/openai/types/responses/response_computer_tool_call_param.py @@ -56,10 +56,16 @@ class ActionClick(TypedDict, total=False): y: Required[int] """The y-coordinate where the click occurred.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + class ActionDoubleClick(TypedDict, total=False): """A double click action.""" + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + type: Required[Literal["double_click"]] """Specifies the event type. @@ -105,6 +111,9 @@ class ActionDrag(TypedDict, total=False): For a drag action, this property is always set to `drag`. """ + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + class ActionKeypress(TypedDict, total=False): """A collection of keypresses the model would like to perform.""" @@ -137,6 +146,9 @@ class ActionMove(TypedDict, total=False): y: Required[int] """The y-coordinate to move to.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + class ActionScreenshot(TypedDict, total=False): """A screenshot action.""" @@ -169,6 +181,9 @@ class ActionScroll(TypedDict, total=False): y: Required[int] """The y-coordinate where the scroll occurred.""" + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + class ActionType(TypedDict, total=False): """An action to type in text.""" From 6e772ae791759b25de83313614e0fb26eba895b7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 20:33:05 +0000 Subject: [PATCH 280/408] fix(api): align SDK response types with expanded item schemas Fixes a variety of missing Items on Responses. OutputItem: add function_call_output, computer_tool_call_output, local_shell_call_output, mcp_approval_response, custom_tool_call_output ItemResource: add reasoning, compaction, custom_tool_call, custom_tool_call_output --- .stats.yml | 6 +-- src/openai/lib/_parsing/_responses.py | 5 +++ src/openai/resources/responses/api.md | 2 + .../types/conversations/conversation_item.py | 2 + src/openai/types/responses/__init__.py | 2 + src/openai/types/responses/parsed_response.py | 12 ++++- ...response_computer_tool_call_output_item.py | 15 ++++--- .../response_custom_tool_call_item.py | 25 +++++++++++ .../response_custom_tool_call_output_item.py | 25 +++++++++++ .../response_function_tool_call_item.py | 13 ++++++ ...response_function_tool_call_output_item.py | 11 +++-- src/openai/types/responses/response_item.py | 8 ++++ .../types/responses/response_output_item.py | 45 +++++++++++++++++++ 13 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 src/openai/types/responses/response_custom_tool_call_item.py create mode 100644 src/openai/types/responses/response_custom_tool_call_output_item.py diff --git a/.stats.yml b/.stats.yml index 297349aa88..b2067da764 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-13599f99dceef322e19171dcc63d90f638d225a445999442249e1ed7a4924c43.yml -openapi_spec_hash: aac8cf8ec3c7dc6d14ecf5dbb289ee7c -config_hash: 96fbf82cf74a44ccd513f5acf0956ffd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-00994178cc8e20d71754b00c54b0e4f5b4128e1c1cce765e9b7d696bd8c80d33.yml +openapi_spec_hash: 81f404053b663f987209b4fb2d08a230 +config_hash: 5635033cdc8c930255f8b529a78de722 diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index df0f52cdc8..8853a0749f 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -107,9 +107,11 @@ def parse_response( or output.type == "compaction" or output.type == "mcp_call" or output.type == "mcp_approval_request" + or output.type == "mcp_approval_response" or output.type == "image_generation_call" or output.type == "code_interpreter_call" or output.type == "local_shell_call" + or output.type == "local_shell_call_output" or output.type == "shell_call" or output.type == "shell_call_output" or output.type == "apply_patch_call" @@ -117,6 +119,9 @@ def parse_response( or output.type == "mcp_list_tools" or output.type == "exec" or output.type == "custom_tool_call" + or output.type == "function_call_output" + or output.type == "computer_call_output" + or output.type == "custom_tool_call_output" ): output_list.append(output) elif TYPE_CHECKING: # type: ignore diff --git a/src/openai/resources/responses/api.md b/src/openai/resources/responses/api.md index d211cd9899..891e0f9796 100644 --- a/src/openai/resources/responses/api.md +++ b/src/openai/resources/responses/api.md @@ -53,7 +53,9 @@ from openai.types.responses import ( ResponseCustomToolCall, ResponseCustomToolCallInputDeltaEvent, ResponseCustomToolCallInputDoneEvent, + ResponseCustomToolCallItem, ResponseCustomToolCallOutput, + ResponseCustomToolCallOutputItem, ResponseError, ResponseErrorEvent, ResponseFailedEvent, diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py index 33bd3ba043..52e87ccb0b 100644 --- a/src/openai/types/conversations/conversation_item.py +++ b/src/openai/types/conversations/conversation_item.py @@ -7,6 +7,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from ..responses.response_reasoning_item import ResponseReasoningItem +from ..responses.response_compaction_item import ResponseCompactionItem from ..responses.response_custom_tool_call import ResponseCustomToolCall from ..responses.response_tool_search_call import ResponseToolSearchCall from ..responses.response_computer_tool_call import ResponseComputerToolCall @@ -234,6 +235,7 @@ class McpCall(BaseModel): ResponseToolSearchCall, ResponseToolSearchOutputItem, ResponseReasoningItem, + ResponseCompactionItem, ResponseCodeInterpreterToolCall, LocalShellCall, LocalShellCallOutput, diff --git a/src/openai/types/responses/__init__.py b/src/openai/types/responses/__init__.py index d06f17f6a3..5e6f45e9b1 100644 --- a/src/openai/types/responses/__init__.py +++ b/src/openai/types/responses/__init__.py @@ -142,6 +142,7 @@ from .web_search_preview_tool_param import WebSearchPreviewToolParam as WebSearchPreviewToolParam from .response_apply_patch_tool_call import ResponseApplyPatchToolCall as ResponseApplyPatchToolCall from .response_compaction_item_param import ResponseCompactionItemParam as ResponseCompactionItemParam +from .response_custom_tool_call_item import ResponseCustomToolCallItem as ResponseCustomToolCallItem from .response_file_search_tool_call import ResponseFileSearchToolCall as ResponseFileSearchToolCall from .response_mcp_call_failed_event import ResponseMcpCallFailedEvent as ResponseMcpCallFailedEvent from .computer_use_preview_tool_param import ComputerUsePreviewToolParam as ComputerUsePreviewToolParam @@ -179,6 +180,7 @@ from .response_audio_transcript_delta_event import ( ResponseAudioTranscriptDeltaEvent as ResponseAudioTranscriptDeltaEvent, ) +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem as ResponseCustomToolCallOutputItem from .container_network_policy_domain_secret import ( ContainerNetworkPolicyDomainSecret as ContainerNetworkPolicyDomainSecret, ) diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index 306e52677d..4100a8d9d0 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -12,7 +12,9 @@ LocalShellCall, McpApprovalRequest, ImageGenerationCall, + McpApprovalResponse, LocalShellCallAction, + LocalShellCallOutput, ) from .response_output_text import ResponseOutputText from .response_output_message import ResponseOutputMessage @@ -30,6 +32,9 @@ from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem +from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput __all__ = ["ParsedResponse", "ParsedResponseOutputMessage", "ParsedResponseOutputText"] @@ -72,22 +77,27 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): ResponseFileSearchToolCall, ResponseFunctionWebSearch, ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, ResponseToolSearchCall, ResponseToolSearchOutputItem, ResponseReasoningItem, McpCall, McpApprovalRequest, + McpApprovalResponse, ImageGenerationCall, LocalShellCall, + LocalShellCallOutput, LocalShellCallAction, McpListTools, ResponseCodeInterpreterToolCall, - ResponseCustomToolCall, ResponseCompactionItem, ResponseFunctionShellToolCall, ResponseFunctionShellToolCallOutput, ResponseApplyPatchToolCall, ResponseApplyPatchToolCallOutput, + ResponseFunctionToolCallOutputItem, + ResponseCustomToolCall, + ResponseCustomToolCallOutputItem, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response_computer_tool_call_output_item.py b/src/openai/types/responses/response_computer_tool_call_output_item.py index 90e935c3bd..bf5555d056 100644 --- a/src/openai/types/responses/response_computer_tool_call_output_item.py +++ b/src/openai/types/responses/response_computer_tool_call_output_item.py @@ -32,6 +32,13 @@ class ResponseComputerToolCallOutputItem(BaseModel): output: ResponseComputerToolCallOutputScreenshot """A computer screenshot image used with the computer use tool.""" + status: Literal["completed", "incomplete", "failed", "in_progress"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + type: Literal["computer_call_output"] """The type of the computer tool call output. Always `computer_call_output`.""" @@ -41,9 +48,5 @@ class ResponseComputerToolCallOutputItem(BaseModel): developer. """ - status: Optional[Literal["in_progress", "completed", "incomplete"]] = None - """The status of the message input. - - One of `in_progress`, `completed`, or `incomplete`. Populated when input items - are returned via API. - """ + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_custom_tool_call_item.py b/src/openai/types/responses/response_custom_tool_call_item.py new file mode 100644 index 0000000000..4f0f930674 --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .response_custom_tool_call import ResponseCustomToolCall + +__all__ = ["ResponseCustomToolCallItem"] + + +class ResponseCustomToolCallItem(ResponseCustomToolCall): + """A call to a custom tool created by the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output_item.py b/src/openai/types/responses/response_custom_tool_call_output_item.py new file mode 100644 index 0000000000..5e5a469e8d --- /dev/null +++ b/src/openai/types/responses/response_custom_tool_call_output_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .response_custom_tool_call_output import ResponseCustomToolCallOutput + +__all__ = ["ResponseCustomToolCallOutputItem"] + + +class ResponseCustomToolCallOutputItem(ResponseCustomToolCallOutput): + """The output of a custom tool call from your code, being sent back to the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call output item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_function_tool_call_item.py b/src/openai/types/responses/response_function_tool_call_item.py index 3df299e512..c8a4488949 100644 --- a/src/openai/types/responses/response_function_tool_call_item.py +++ b/src/openai/types/responses/response_function_tool_call_item.py @@ -1,5 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional +from typing_extensions import Literal + from .response_function_tool_call import ResponseFunctionToolCall __all__ = ["ResponseFunctionToolCallItem"] @@ -14,3 +17,13 @@ class ResponseFunctionToolCallItem(ResponseFunctionToolCall): id: str # type: ignore """The unique ID of the function tool call.""" + + status: Literal["in_progress", "completed", "incomplete"] # type: ignore + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_function_tool_call_output_item.py b/src/openai/types/responses/response_function_tool_call_output_item.py index 1a2c848cb3..e40feeb37e 100644 --- a/src/openai/types/responses/response_function_tool_call_output_item.py +++ b/src/openai/types/responses/response_function_tool_call_output_item.py @@ -29,12 +29,15 @@ class ResponseFunctionToolCallOutputItem(BaseModel): list of output content. """ - type: Literal["function_call_output"] - """The type of the function tool call output. Always `function_call_output`.""" - - status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + status: Literal["in_progress", "completed", "incomplete"] """The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when items are returned via API. """ + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py index 316a912f62..721bf02ecb 100644 --- a/src/openai/types/responses/response_item.py +++ b/src/openai/types/responses/response_item.py @@ -6,17 +6,21 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from .response_output_message import ResponseOutputMessage +from .response_reasoning_item import ResponseReasoningItem +from .response_compaction_item import ResponseCompactionItem from .response_tool_search_call import ResponseToolSearchCall from .response_computer_tool_call import ResponseComputerToolCall from .response_input_message_item import ResponseInputMessageItem from .response_function_web_search import ResponseFunctionWebSearch from .response_apply_patch_tool_call import ResponseApplyPatchToolCall +from .response_custom_tool_call_item import ResponseCustomToolCallItem from .response_file_search_tool_call import ResponseFileSearchToolCall from .response_function_tool_call_item import ResponseFunctionToolCallItem from .response_tool_search_output_item import ResponseToolSearchOutputItem from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput @@ -231,6 +235,8 @@ class McpCall(BaseModel): ResponseFunctionToolCallOutputItem, ResponseToolSearchCall, ResponseToolSearchOutputItem, + ResponseReasoningItem, + ResponseCompactionItem, ImageGenerationCall, ResponseCodeInterpreterToolCall, LocalShellCall, @@ -243,6 +249,8 @@ class McpCall(BaseModel): McpApprovalRequest, McpApprovalResponse, McpCall, + ResponseCustomToolCallItem, + ResponseCustomToolCallOutputItem, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index 4be4cbf78e..a4b23f26fd 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -19,6 +19,9 @@ from .response_function_shell_tool_call import ResponseFunctionShellToolCall from .response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall from .response_apply_patch_tool_call_output import ResponseApplyPatchToolCallOutput +from .response_custom_tool_call_output_item import ResponseCustomToolCallOutputItem +from .response_computer_tool_call_output_item import ResponseComputerToolCallOutputItem +from .response_function_tool_call_output_item import ResponseFunctionToolCallOutputItem from .response_function_shell_tool_call_output import ResponseFunctionShellToolCallOutput __all__ = [ @@ -26,10 +29,12 @@ "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", + "LocalShellCallOutput", "McpCall", "McpListTools", "McpListToolsTool", "McpApprovalRequest", + "McpApprovalResponse", ] @@ -90,6 +95,22 @@ class LocalShellCall(BaseModel): """The type of the local shell call. Always `local_shell_call`.""" +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + class McpCall(BaseModel): """An invocation of a tool on an MCP server.""" @@ -182,13 +203,34 @@ class McpApprovalRequest(BaseModel): """The type of the item. Always `mcp_approval_request`.""" +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + ResponseOutputItem: TypeAlias = Annotated[ Union[ ResponseOutputMessage, ResponseFileSearchToolCall, ResponseFunctionToolCall, + ResponseFunctionToolCallOutputItem, ResponseFunctionWebSearch, ResponseComputerToolCall, + ResponseComputerToolCallOutputItem, ResponseReasoningItem, ResponseToolSearchCall, ResponseToolSearchOutputItem, @@ -196,6 +238,7 @@ class McpApprovalRequest(BaseModel): ImageGenerationCall, ResponseCodeInterpreterToolCall, LocalShellCall, + LocalShellCallOutput, ResponseFunctionShellToolCall, ResponseFunctionShellToolCallOutput, ResponseApplyPatchToolCall, @@ -203,7 +246,9 @@ class McpApprovalRequest(BaseModel): McpCall, McpListTools, McpApprovalRequest, + McpApprovalResponse, ResponseCustomToolCall, + ResponseCustomToolCallOutputItem, ], PropertyInfo(discriminator="type"), ] From 5ae2cc10e4140d36aa236fa7c0bc5ce5ff190a01 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 20:33:44 +0000 Subject: [PATCH 281/408] release: 2.30.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3a5058bfce..ef8743735a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.29.0" + ".": "2.30.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index eea76f7709..785fab5782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 2.30.0 (2026-03-25) + +Full Changelog: [v2.29.0...v2.30.0](https://github.com/openai/openai-python/compare/v2.29.0...v2.30.0) + +### Features + +* **api:** add keys field to Click/DoubleClick/Drag/Move/Scroll computer actions ([ee1bbed](https://github.com/openai/openai-python/commit/ee1bbeddbb38dab817557412dc106354409bb950)) + + +### Bug Fixes + +* **api:** align SDK response types with expanded item schemas ([f3f258a](https://github.com/openai/openai-python/commit/f3f258a9d4d19db3fb0c6c35e25ad3cedbe71254)) +* sanitize endpoint path params ([89f6698](https://github.com/openai/openai-python/commit/89f66988fde790c0c83ff8b876d1e1b10d616367)) +* **types:** make type required in ResponseInputMessageItem ([cfdb167](https://github.com/openai/openai-python/commit/cfdb1676ea0550840330a58f1a31a40a41a0a53f)) + + +### Chores + +* **ci:** skip lint on metadata-only changes ([faa93e1](https://github.com/openai/openai-python/commit/faa93e19a1d5c30c7dd672a08dbbdbb3c0374714)) +* **internal:** update gitignore ([c468477](https://github.com/openai/openai-python/commit/c468477f1546579618865a726e35a685cffeacd9)) +* **tests:** bump steady to v0.19.4 ([f350af8](https://github.com/openai/openai-python/commit/f350af86c13ade0237778010d264c55fda443354)) +* **tests:** bump steady to v0.19.5 ([5c03401](https://github.com/openai/openai-python/commit/5c0340128fc1a416e2dfdc6ab4b05f1e954e8482)) +* **tests:** bump steady to v0.19.6 ([b6353b8](https://github.com/openai/openai-python/commit/b6353b8411d31dcc95875d801ce9e90a21e0fd52)) +* **tests:** bump steady to v0.19.7 ([1d654be](https://github.com/openai/openai-python/commit/1d654bea74ac9c3d43302587f98f33cfff502e48)) + + +### Refactors + +* **tests:** switch from prism to steady ([4a82035](https://github.com/openai/openai-python/commit/4a82035669b739d16a0e85d4ded778d51e061948)) + ## 2.29.0 (2026-03-17) Full Changelog: [v2.28.0...v2.29.0](https://github.com/openai/openai-python/compare/v2.28.0...v2.29.0) diff --git a/pyproject.toml b/pyproject.toml index 46a72007df..bfc0e13be7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.29.0" +version = "2.30.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b8a1b37e13..788e82e056 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.29.0" # x-release-please-version +__version__ = "2.30.0" # x-release-please-version From 58184ad545ee2abd98e171ee09766f259d7f38cd Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Fri, 27 Mar 2026 20:16:28 -0500 Subject: [PATCH 282/408] Pin GitHub Actions workflow references (#3021) --- .github/workflows/ci.yml | 10 +++++----- .github/workflows/create-releases.yml | 4 ++-- .github/workflows/detect-breaking-changes.yml | 8 ++++---- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b786cbcf6a..414f8ce856 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install Rye run: | @@ -46,7 +46,7 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install Rye run: | @@ -67,7 +67,7 @@ jobs: github.repository == 'stainless-sdks/openai-python' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -87,7 +87,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install Rye run: | @@ -110,7 +110,7 @@ jobs: if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install Rye run: | diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index f0ef434d02..98b7f20ffe 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -14,9 +14,9 @@ jobs: environment: publish steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: stainless-api/trigger-release-please@v1 + - uses: stainless-api/trigger-release-please@bb6677c5a04578eec1ccfd9e1913b5b78ed64c61 # v1 id: release with: repo: ${{ github.event.repository.full_name }} diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index 87c02061c2..bd73bb6e2a 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -15,7 +15,7 @@ jobs: run: | echo "FETCH_DEPTH=$(expr ${{ github.event.pull_request.commits }} + 1)" >> $GITHUB_ENV - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: # Ensure we can check out the pull request base in the script below. fetch-depth: ${{ env.FETCH_DEPTH }} @@ -45,7 +45,7 @@ jobs: if: github.repository == 'openai/openai-python' steps: # Setup this sdk - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: path: openai-python @@ -64,13 +64,13 @@ jobs: rye sync --all-features # Setup the agents lib - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: repository: openai/openai-agents-python path: openai-agents-python - name: Setup uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 with: enable-cache: true diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index b9c959ecf3..f67347f2fa 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -11,7 +11,7 @@ jobs: environment: publish steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install Rye run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index be7db8df12..503de9d99a 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -13,7 +13,7 @@ jobs: if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Check release environment run: | From d392aae08a2e2f0fdc3eb8985944102a86913694 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 19:29:58 +0000 Subject: [PATCH 283/408] feat(internal): implement indices array format for query and form serialization --- scripts/mock | 4 ++-- scripts/test | 2 +- src/openai/_qs.py | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 9ecceca0a7..4931f304c7 100755 --- a/scripts/mock +++ b/scripts/mock @@ -24,7 +24,7 @@ if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 9231513853..52bcc4cec8 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=brackets --validator-query-array-format=brackets --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 diff --git a/src/openai/_qs.py b/src/openai/_qs.py index ada6fd3f72..de8c99bc63 100644 --- a/src/openai/_qs.py +++ b/src/openai/_qs.py @@ -101,7 +101,10 @@ def _stringify_item( items.extend(self._stringify_item(key, item, opts)) return items elif array_format == "indices": - raise NotImplementedError("The array indices format is not supported yet") + items = [] + for i, item in enumerate(value): + items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) + return items elif array_format == "brackets": items = [] key = key + "[]" From ad7cc79c80b7ddffb03c0339be05f468ae46d54f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 20:46:42 +0000 Subject: [PATCH 284/408] docs(api): update file parameter descriptions in vector_stores files and file_batches --- .stats.yml | 4 ++-- .../resources/vector_stores/file_batches.py | 20 +++++++++++-------- src/openai/resources/vector_stores/files.py | 8 ++++++-- .../vector_stores/file_batch_create_params.py | 14 ++++++++----- .../types/vector_stores/file_create_params.py | 4 +++- 5 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.stats.yml b/.stats.yml index b2067da764..7fb8cd473d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-00994178cc8e20d71754b00c54b0e4f5b4128e1c1cce765e9b7d696bd8c80d33.yml -openapi_spec_hash: 81f404053b663f987209b4fb2d08a230 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-24647ccd7356fee965aaca347476460727c6aec762e16a9eb41950d6fbccf0be.yml +openapi_spec_hash: ef99e305f20ae8ae7b2758a205280cca config_hash: 5635033cdc8c930255f8b529a78de722 diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index f097cf8a92..1ffd7642c0 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -79,14 +79,16 @@ def create( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied - to all files in the batch. The maximum batch size is 2000 files. Mutually - exclusive with `files`. + to all files in the batch. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `files`. files: A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must - be specified for each file. The maximum batch size is 2000 files. Mutually - exclusive with `file_ids`. + be specified for each file. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `file_ids`. extra_headers: Send extra headers @@ -452,14 +454,16 @@ async def create( file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied - to all files in the batch. The maximum batch size is 2000 files. Mutually - exclusive with `files`. + to all files in the batch. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `files`. files: A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must - be specified for each file. The maximum batch size is 2000 files. Mutually - exclusive with `file_ids`. + be specified for each file. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `file_ids`. extra_headers: Send extra headers diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py index 8666434587..3ef6137267 100644 --- a/src/openai/resources/vector_stores/files.py +++ b/src/openai/resources/vector_stores/files.py @@ -67,7 +67,9 @@ def create( Args: file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID that the vector store should use. Useful for tools like `file_search` that can access - files. + files. For multi-file ingestion, we recommend + [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) + to minimize per-vector-store write requests. attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and @@ -498,7 +500,9 @@ async def create( Args: file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID that the vector store should use. Useful for tools like `file_search` that can access - files. + files. For multi-file ingestion, we recommend + [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) + to minimize per-vector-store write requests. attributes: Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and diff --git a/src/openai/types/vector_stores/file_batch_create_params.py b/src/openai/types/vector_stores/file_batch_create_params.py index 7ca0de81da..1e578888c5 100644 --- a/src/openai/types/vector_stores/file_batch_create_params.py +++ b/src/openai/types/vector_stores/file_batch_create_params.py @@ -33,8 +33,9 @@ class FileBatchCreateParams(TypedDict, total=False): A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files. If `attributes` or `chunking_strategy` are provided, they will be applied - to all files in the batch. The maximum batch size is 2000 files. Mutually - exclusive with `files`. + to all files in the batch. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `files`. """ files: Iterable[File] @@ -42,8 +43,9 @@ class FileBatchCreateParams(TypedDict, total=False): A list of objects that each include a `file_id` plus optional `attributes` or `chunking_strategy`. Use this when you need to override metadata for specific files. The global `attributes` or `chunking_strategy` will be ignored and must - be specified for each file. The maximum batch size is 2000 files. Mutually - exclusive with `file_ids`. + be specified for each file. The maximum batch size is 2000 files. This endpoint + is recommended for multi-file ingestion and helps reduce per-vector-store write + request pressure. Mutually exclusive with `file_ids`. """ @@ -52,7 +54,9 @@ class File(TypedDict, total=False): """ A [File](https://platform.openai.com/docs/api-reference/files) ID that the vector store should use. Useful for tools like `file_search` that can access - files. + files. For multi-file ingestion, we recommend + [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) + to minimize per-vector-store write requests. """ attributes: Optional[Dict[str, Union[str, float, bool]]] diff --git a/src/openai/types/vector_stores/file_create_params.py b/src/openai/types/vector_stores/file_create_params.py index 5b8989251a..530adee8f6 100644 --- a/src/openai/types/vector_stores/file_create_params.py +++ b/src/openai/types/vector_stores/file_create_params.py @@ -15,7 +15,9 @@ class FileCreateParams(TypedDict, total=False): """ A [File](https://platform.openai.com/docs/api-reference/files) ID that the vector store should use. Useful for tools like `file_search` that can access - files. + files. For multi-file ingestion, we recommend + [`file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) + to minimize per-vector-store write requests. """ attributes: Optional[Dict[str, Union[str, float, bool]]] From c0c59afa39a82f73063a52f624a9a4a2a6bf3313 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:13:17 +0000 Subject: [PATCH 285/408] fix(types): remove web_search_call.results from ResponseIncludable --- .stats.yml | 4 ++-- src/openai/resources/realtime/calls.py | 10 ++++++---- src/openai/types/realtime/call_accept_params.py | 5 +++-- .../types/realtime/realtime_session_create_request.py | 5 +++-- .../realtime/realtime_session_create_request_param.py | 5 +++-- .../types/realtime/realtime_session_create_response.py | 5 +++-- src/openai/types/responses/response_includable.py | 1 - 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7fb8cd473d..4d731cc503 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-24647ccd7356fee965aaca347476460727c6aec762e16a9eb41950d6fbccf0be.yml -openapi_spec_hash: ef99e305f20ae8ae7b2758a205280cca +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2fab88288cbbe872f5d61d1d47da2286662a123b4312bc7fc36addba6607cd67.yml +openapi_spec_hash: a7ee80374e409ed9ecc8ea2e3cd31071 config_hash: 5635033cdc8c930255f8b529a78de722 diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index f34748d239..8fa2569a96 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -193,8 +193,9 @@ def accept( tools: Tools available to the model. tracing: Realtime API can write session traces to the - [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once - tracing is enabled for a session, the configuration cannot be modified. + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. @@ -522,8 +523,9 @@ async def accept( tools: Tools available to the model. tracing: Realtime API can write session traces to the - [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once - tracing is enabled for a session, the configuration cannot be modified. + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index 6d8caf9306..1baddbfc2c 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -101,8 +101,9 @@ class CallAcceptParams(TypedDict, total=False): tracing: Optional[RealtimeTracingConfigParam] """ Realtime API can write session traces to the - [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once - tracing is enabled for a session, the configuration cannot be modified. + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index e34136a10a..163a0d16d8 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -103,8 +103,9 @@ class RealtimeSessionCreateRequest(BaseModel): tracing: Optional[RealtimeTracingConfig] = None """ Realtime API can write session traces to the - [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once - tracing is enabled for a session, the configuration cannot be modified. + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index f3180c9ed6..19c73b909b 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -103,8 +103,9 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): tracing: Optional[RealtimeTracingConfigParam] """ Realtime API can write session traces to the - [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once - tracing is enabled for a session, the configuration cannot be modified. + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 3c3bef93f4..e2ed5ddce5 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -509,8 +509,9 @@ class RealtimeSessionCreateResponse(BaseModel): tracing: Optional[Tracing] = None """ Realtime API can write session traces to the - [Traces Dashboard](/logs?api=traces). Set to null to disable tracing. Once - tracing is enabled for a session, the configuration cannot be modified. + [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to + disable tracing. Once tracing is enabled for a session, the configuration cannot + be modified. `auto` will create a trace for the session with default values for the workflow name, group id, and metadata. diff --git a/src/openai/types/responses/response_includable.py b/src/openai/types/responses/response_includable.py index 675c83405a..06e56be8a7 100644 --- a/src/openai/types/responses/response_includable.py +++ b/src/openai/types/responses/response_includable.py @@ -6,7 +6,6 @@ ResponseIncludable: TypeAlias = Literal[ "file_search_call.results", - "web_search_call.results", "web_search_call.action.sources", "message.input_image.image_url", "computer_call_output.output.image_url", From 2076d85f9226113e4ba360a7f456091988092dbf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:45:02 +0000 Subject: [PATCH 286/408] feat(api): add phase field to conversations message --- .stats.yml | 4 ++-- src/openai/types/conversations/message.py | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4d731cc503..de471b8f60 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-2fab88288cbbe872f5d61d1d47da2286662a123b4312bc7fc36addba6607cd67.yml -openapi_spec_hash: a7ee80374e409ed9ecc8ea2e3cd31071 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-89e54b8e2c185d30e869f73e7798308d56a6a835a675d54628dd86836f147879.yml +openapi_spec_hash: 85b0dd465aa1a034f2764b0758671f21 config_hash: 5635033cdc8c930255f8b529a78de722 diff --git a/src/openai/types/conversations/message.py b/src/openai/types/conversations/message.py index 86c8860da8..075f5bd290 100644 --- a/src/openai/types/conversations/message.py +++ b/src/openai/types/conversations/message.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union +from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -68,3 +68,11 @@ class Message(BaseModel): type: Literal["message"] """The type of the message. Always set to `message`.""" + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ From 6efca95a76f6ca9cb91fdf536c6c9ebcef075541 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:33:41 +0000 Subject: [PATCH 287/408] chore(tests): bump steady to v0.20.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 4931f304c7..8b82c3e59c 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.7 -- steady --version + npm exec --package=@stdy/cli@0.20.1 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 52bcc4cec8..ed64d32077 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From de2c7b1d087f41f33ada85a7460f32e55331778a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:26:08 +0000 Subject: [PATCH 288/408] chore(tests): bump steady to v0.20.2 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 8b82c3e59c..886f2ffc14 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.1 -- steady --version + npm exec --package=@stdy/cli@0.20.2 -- steady --version - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index ed64d32077..57cabda6ae 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 454b2575d59a086f279d99dc791058acee2f14c0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2026 04:45:12 +0000 Subject: [PATCH 289/408] feat(api): add web_search_call.results to ResponseIncludable type --- .stats.yml | 4 ++-- src/openai/types/responses/response_includable.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index de471b8f60..b542dc82ac 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-89e54b8e2c185d30e869f73e7798308d56a6a835a675d54628dd86836f147879.yml -openapi_spec_hash: 85b0dd465aa1a034f2764b0758671f21 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-dd99495ad509338e6de862802839360dfe394d5cd6d6ba6d13fec8fca92328b8.yml +openapi_spec_hash: 68abda9122013a9ae3f084cfdbe8e8c1 config_hash: 5635033cdc8c930255f8b529a78de722 diff --git a/src/openai/types/responses/response_includable.py b/src/openai/types/responses/response_includable.py index 06e56be8a7..675c83405a 100644 --- a/src/openai/types/responses/response_includable.py +++ b/src/openai/types/responses/response_includable.py @@ -6,6 +6,7 @@ ResponseIncludable: TypeAlias = Literal[ "file_search_call.results", + "web_search_call.results", "web_search_call.action.sources", "message.input_image.image_url", "computer_call_output.output.image_url", From 73ea2f75ba57a1db964518b33b790b1e1251b8d5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:09:47 +0000 Subject: [PATCH 290/408] fix(client): preserve hardcoded query params when merging with user params --- src/openai/_base_client.py | 4 ++++ tests/test_client.py | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index cf4571bf45..148e273135 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -542,6 +542,10 @@ def _build_request( files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} diff --git a/tests/test_client.py b/tests/test_client.py index a015cd7d40..04ef6794ed 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -434,6 +434,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: OpenAI) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: OpenAI) -> None: request = client._build_request( FinalRequestOptions( @@ -1466,6 +1490,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncOpenAI) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: OpenAI) -> None: request = client._build_request( FinalRequestOptions( From f1fd4fae0329ee3df2f1bb25d93f51311782ad1a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:06:02 +0000 Subject: [PATCH 291/408] feat(client): support sending raw data over websockets --- src/openai/resources/realtime/realtime.py | 6 ++++++ src/openai/resources/responses/responses.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 73a87fc2e7..82c9815b03 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -292,6 +292,9 @@ async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> N ) await self._connection.send(data) + async def send_raw(self, data: bytes | str) -> None: + await self._connection.send(data) + async def close(self, *, code: int = 1000, reason: str = "") -> None: await self._connection.close(code=code, reason=reason) @@ -483,6 +486,9 @@ def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: ) self._connection.send(data) + def send_raw(self, data: bytes | str) -> None: + self._connection.send(data) + def close(self, *, code: int = 1000, reason: str = "") -> None: self._connection.close(code=code, reason=reason) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 63795f95a9..360c5afa1a 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -3632,6 +3632,9 @@ async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> ) await self._connection.send(data) + async def send_raw(self, data: bytes | str) -> None: + await self._connection.send(data) + async def close(self, *, code: int = 1000, reason: str = "") -> None: await self._connection.close(code=code, reason=reason) @@ -3798,6 +3801,9 @@ def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: ) self._connection.send(data) + def send_raw(self, data: bytes | str) -> None: + self._connection.send(data) + def close(self, *, code: int = 1000, reason: str = "") -> None: self._connection.close(code=code, reason=reason) From 5be95364a5a82746cb7b1c77df10dfaf138496bb Mon Sep 17 00:00:00 2001 From: Kar Petrosyan <92274156+karpetrosyan@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:57:12 +0400 Subject: [PATCH 292/408] feat(client): add support for short-lived tokens (#1608) Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .stats.yml | 4 +- README.md | 103 ++++++ api.md | 1 + src/openai/__init__.py | 2 + src/openai/_base_client.py | 31 +- src/openai/_client.py | 159 +++++++-- src/openai/_exceptions.py | 28 ++ src/openai/auth/__init__.py | 19 ++ src/openai/auth/_workload.py | 313 +++++++++++++++++ src/openai/lib/azure.py | 9 + src/openai/types/__init__.py | 1 + src/openai/types/shared/__init__.py | 1 + src/openai/types/shared/oauth_error_code.py | 8 + src/openai/types/shared_params/__init__.py | 1 + .../types/shared_params/oauth_error_code.py | 10 + tests/test_auth.py | 190 +++++++++++ tests/test_client.py | 323 +++++++++++++++++- 17 files changed, 1170 insertions(+), 33 deletions(-) create mode 100644 src/openai/auth/__init__.py create mode 100644 src/openai/auth/_workload.py create mode 100644 src/openai/types/shared/oauth_error_code.py create mode 100644 src/openai/types/shared_params/oauth_error_code.py create mode 100644 tests/test_auth.py diff --git a/.stats.yml b/.stats.yml index b542dc82ac..461d4c7c07 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-dd99495ad509338e6de862802839360dfe394d5cd6d6ba6d13fec8fca92328b8.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a6eca1bd01e0c434af356fe5275c206057216a4e626d1051d294c27016cd6d05.yml openapi_spec_hash: 68abda9122013a9ae3f084cfdbe8e8c1 -config_hash: 5635033cdc8c930255f8b529a78de722 +config_hash: 4975e16a94e8f9901428022044131888 diff --git a/README.md b/README.md index 7e4f0ae657..9450c0bc51 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,109 @@ to add `OPENAI_API_KEY="My API Key"` to your `.env` file so that your API key is not stored in source control. [Get an API key here](https://platform.openai.com/settings/organization/api-keys). +### Workload Identity Authentication + +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. + +#### Kubernetes (service account tokens) + +```python +from openai import OpenAI +from openai.auth import k8s_service_account_token_provider + +client = OpenAI( + workload_identity={ + "client_id": "your-client-id", + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": k8s_service_account_token_provider( + "/var/run/secrets/kubernetes.io/serviceaccount/token" + ), + }, + organization="org-xyz", + project="proj-abc", +) + +response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}], +) +``` + +#### Azure (managed identity) + +```python +from openai import OpenAI +from openai.auth import azure_managed_identity_token_provider + +client = OpenAI( + workload_identity={ + "client_id": "your-client-id", + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": azure_managed_identity_token_provider( + resource="https://management.azure.com/", + ), + }, +) +``` + +#### Google Cloud Platform (compute engine metadata) + +```python +from openai import OpenAI +from openai.auth import gcp_id_token_provider + +client = OpenAI( + workload_identity={ + "client_id": "your-client-id", + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": gcp_id_token_provider(audience="https://api.openai.com/v1"), + }, +) +``` + +#### Custom subject token provider + +```python +from openai import OpenAI + + +def get_custom_token() -> str: + return "your-jwt-token" + + +client = OpenAI( + workload_identity={ + "client_id": "your-client-id", + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": { + "token_type": "jwt", + "get_token": get_custom_token, + }, + } +) +``` + +You can also customize the token refresh buffer (default is 1200 seconds (20 minutes) before expiration): + +```python +from openai import OpenAI +from openai.auth import k8s_service_account_token_provider + +client = OpenAI( + workload_identity={ + "client_id": "your-client-id", + "identity_provider_id": "idp-123", + "service_account_id": "sa-456", + "provider": k8s_service_account_token_provider("/var/token"), + "refresh_buffer_seconds": 120.0, + } +) +``` + ### Vision With an image URL: diff --git a/api.md b/api.md index 852df5bb8a..decf4e0129 100644 --- a/api.md +++ b/api.md @@ -11,6 +11,7 @@ from openai.types import ( FunctionDefinition, FunctionParameters, Metadata, + OAuthErrorCode, Reasoning, ReasoningEffort, ResponseFormatJSONObject, diff --git a/src/openai/__init__.py b/src/openai/__init__.py index b2093ada68..3e0a135929 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -16,6 +16,7 @@ from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS from ._exceptions import ( APIError, + OAuthError, OpenAIError, ConflictError, NotFoundError, @@ -57,6 +58,7 @@ "APIResponseValidationError", "BadRequestError", "AuthenticationError", + "OAuthError", "PermissionDeniedError", "NotFoundError", "ConflictError", diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 148e273135..a1d0960700 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -30,7 +30,7 @@ cast, overload, ) -from typing_extensions import Literal, override, get_origin +from typing_extensions import Unpack, Literal, override, get_origin import anyio import httpx @@ -81,6 +81,7 @@ ) from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder from ._exceptions import ( + OpenAIError, APIStatusError, APITimeoutError, APIConnectionError, @@ -936,6 +937,15 @@ def _prepare_request( """ return None + def _send_request( + self, + request: httpx.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + return self._client.send(request, stream=stream, **kwargs) + @overload def request( self, @@ -1006,7 +1016,7 @@ def request( response = None try: - response = self._client.send( + response = self._send_request( request, stream=stream or self._should_stream_response_body(request=request), **kwargs, @@ -1025,6 +1035,9 @@ def request( log.debug("Raising timeout error") raise APITimeoutError(request=request) from err + except OpenAIError as err: + # Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError + raise err except Exception as err: log.debug("Encountered Exception", exc_info=True) @@ -1530,6 +1543,15 @@ async def _prepare_request( """ return None + async def _send_request( + self, + request: httpx.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + return await self._client.send(request, stream=stream, **kwargs) + @overload async def request( self, @@ -1605,7 +1627,7 @@ async def request( response = None try: - response = await self._client.send( + response = await self._send_request( request, stream=stream or self._should_stream_response_body(request=request), **kwargs, @@ -1624,6 +1646,9 @@ async def request( log.debug("Raising timeout error") raise APITimeoutError(request=request) from err + except OpenAIError as err: + # Propagate OpenAIErrors as-is, without retrying or wrapping in APIConnectionError + raise err except Exception as err: log.debug("Encountered Exception", exc_info=True) diff --git a/src/openai/_client.py b/src/openai/_client.py index aadf3601f2..434f957e19 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -4,18 +4,20 @@ import os from typing import TYPE_CHECKING, Any, Mapping, Callable, Awaitable -from typing_extensions import Self, override +from typing_extensions import Self, Unpack, override import httpx from . import _exceptions from ._qs import Querystring +from .auth import WorkloadIdentity, WorkloadIdentityAuth from ._types import ( Omit, Timeout, NotGiven, Transport, ProxiesTypes, + HttpxSendArgs, RequestOptions, not_given, ) @@ -82,13 +84,17 @@ __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "OpenAI", "AsyncOpenAI", "Client", "AsyncClient"] +WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER = "workload-identity-auth" + class OpenAI(SyncAPIClient): # client options api_key: str + workload_identity: WorkloadIdentity | None organization: str | None project: str | None webhook_secret: str | None + _workload_identity_auth: WorkloadIdentityAuth | None websocket_base_url: str | httpx.URL | None """Base URL for WebSocket connections. @@ -102,6 +108,7 @@ def __init__( self, *, api_key: str | None | Callable[[], str] = None, + workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -133,18 +140,31 @@ def __init__( - `project` from `OPENAI_PROJECT_ID` - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` """ - if api_key is None: - api_key = os.environ.get("OPENAI_API_KEY") - if api_key is None: - raise OpenAIError( - "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" + if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None: + raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive") + + self.workload_identity = workload_identity + + if workload_identity is not None: + self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER + self._api_key_provider = None + self._workload_identity_auth = WorkloadIdentityAuth( + workload_identity=workload_identity, ) - if callable(api_key): - self.api_key = "" - self._api_key_provider: Callable[[], str] | None = api_key else: - self.api_key = api_key - self._api_key_provider = None + if api_key is None: + api_key = os.environ.get("OPENAI_API_KEY") + if api_key is None: + raise OpenAIError( + "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" + ) + if callable(api_key): + self.api_key = "" + self._api_key_provider: Callable[[], str] | None = api_key # type: ignore[no-redef] + else: + self.api_key = api_key + self._api_key_provider = None + self._workload_identity_auth = None if organization is None: organization = os.environ.get("OPENAI_ORG_ID") @@ -344,11 +364,46 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: self._refresh_api_key() return super()._prepare_options(options) + def _send_with_auth_retry( + self, + request: httpx.Request, + *, + stream: bool, + retried: bool = False, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + if self._workload_identity_auth: + request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" + + response = super()._send_request(request, stream=stream, **kwargs) + + if not retried and response.status_code == 401 and self._workload_identity_auth: + response.close() + + self._workload_identity_auth.invalidate_token() + fresh_token = self._workload_identity_auth.get_token() + + request.headers["Authorization"] = f"Bearer {fresh_token}" + + return self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) + + return response + + @override + def _send_request( + self, + request: httpx.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + return self._send_with_auth_retry(request, stream=stream, **kwargs) + @property @override def auth_headers(self) -> dict[str, str]: api_key = self.api_key - if not api_key: + if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: # if the api key is an empty string, encoding the header will fail return {} return {"Authorization": f"Bearer {api_key}"} @@ -368,6 +423,7 @@ def copy( self, *, api_key: str | Callable[[], str] | None = None, + workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -404,8 +460,10 @@ def copy( params = set_default_query http_client = http_client or self._client + return self.__class__( api_key=api_key or self._api_key_provider or self.api_key, + workload_identity=workload_identity or self.workload_identity, organization=organization or self.organization, project=project or self.project, webhook_secret=webhook_secret or self.webhook_secret, @@ -461,9 +519,11 @@ def _make_status_error( class AsyncOpenAI(AsyncAPIClient): # client options api_key: str + workload_identity: WorkloadIdentity | None organization: str | None project: str | None webhook_secret: str | None + _workload_identity_auth: WorkloadIdentityAuth | None websocket_base_url: str | httpx.URL | None """Base URL for WebSocket connections. @@ -476,7 +536,8 @@ class AsyncOpenAI(AsyncAPIClient): def __init__( self, *, - api_key: str | Callable[[], Awaitable[str]] | None = None, + api_key: str | None | Callable[[], Awaitable[str]] = None, + workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -508,18 +569,31 @@ def __init__( - `project` from `OPENAI_PROJECT_ID` - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` """ - if api_key is None: - api_key = os.environ.get("OPENAI_API_KEY") - if api_key is None: - raise OpenAIError( - "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" + if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None: + raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive") + + self.workload_identity = workload_identity + + if workload_identity is not None: + self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER + self._api_key_provider = None + self._workload_identity_auth = WorkloadIdentityAuth( + workload_identity=workload_identity, ) - if callable(api_key): - self.api_key = "" - self._api_key_provider: Callable[[], Awaitable[str]] | None = api_key else: - self.api_key = api_key - self._api_key_provider = None + if api_key is None: + api_key = os.environ.get("OPENAI_API_KEY") + if api_key is None: + raise OpenAIError( + "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" + ) + if callable(api_key): + self.api_key = "" + self._api_key_provider: Callable[[], Awaitable[str]] | None = api_key # type: ignore[no-redef] + else: + self.api_key = api_key + self._api_key_provider = None + self._workload_identity_auth = None if organization is None: organization = os.environ.get("OPENAI_ORG_ID") @@ -719,11 +793,46 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp await self._refresh_api_key() return await super()._prepare_options(options) + async def _send_with_auth_retry( + self, + request: httpx.Request, + *, + stream: bool, + retried: bool = False, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + if self._workload_identity_auth: + request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" + + response = await super()._send_request(request, stream=stream, **kwargs) + + if not retried and response.status_code == 401 and self._workload_identity_auth: + await response.aclose() + + self._workload_identity_auth.invalidate_token() + fresh_token = await self._workload_identity_auth.get_token_async() + + request.headers["Authorization"] = f"Bearer {fresh_token}" + + return await self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) + + return response + + @override + async def _send_request( + self, + request: httpx.Request, + *, + stream: bool, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + return await self._send_with_auth_retry(request, stream=stream, **kwargs) + @property @override def auth_headers(self) -> dict[str, str]: api_key = self.api_key - if not api_key: + if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: # if the api key is an empty string, encoding the header will fail return {} return {"Authorization": f"Bearer {api_key}"} @@ -743,6 +852,7 @@ def copy( self, *, api_key: str | Callable[[], Awaitable[str]] | None = None, + workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -781,6 +891,7 @@ def copy( http_client = http_client or self._client return self.__class__( api_key=api_key or self._api_key_provider or self.api_key, + workload_identity=workload_identity or self.workload_identity, organization=organization or self.organization, project=project or self.project, webhook_secret=webhook_secret or self.webhook_secret, diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index 09016dfedb..a37ed8ca82 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -9,6 +9,7 @@ from ._utils import is_dict from ._models import construct_type +from .types.shared.oauth_error_code import OAuthErrorCode if TYPE_CHECKING: from .types.chat import ChatCompletion @@ -16,6 +17,7 @@ __all__ = [ "BadRequestError", "AuthenticationError", + "OAuthError", "PermissionDeniedError", "NotFoundError", "ConflictError", @@ -25,6 +27,7 @@ "LengthFinishReasonError", "ContentFilterFinishReasonError", "InvalidWebhookSignatureError", + "SubjectTokenProviderError", ] @@ -32,6 +35,14 @@ class OpenAIError(Exception): pass +class SubjectTokenProviderError(OpenAIError): + response: httpx.Response | None + + def __init__(self, message: str, *, response: httpx.Response | None = None) -> None: + super().__init__(message) + self.response = response + + class APIError(OpenAIError): message: str request: httpx.Request @@ -109,6 +120,23 @@ class AuthenticationError(APIStatusError): status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] +class OAuthError(AuthenticationError): + error: Optional[OAuthErrorCode] + + def __init__(self, *, response: httpx.Response, body: object | None) -> None: + message = "OAuth authentication error." + error = None + + if is_dict(body): + error = body.get("error") + description = body.get("error_description") + if description and isinstance(description, str): + message = description + + super().__init__(message, response=response, body=body) + self.error = cast(Optional[OAuthErrorCode], error) + + class PermissionDeniedError(APIStatusError): status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] diff --git a/src/openai/auth/__init__.py b/src/openai/auth/__init__.py new file mode 100644 index 0000000000..367aa86b72 --- /dev/null +++ b/src/openai/auth/__init__.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from ._workload import ( + WorkloadIdentity as WorkloadIdentity, + SubjectTokenProvider as SubjectTokenProvider, + WorkloadIdentityAuth as WorkloadIdentityAuth, + gcp_id_token_provider as gcp_id_token_provider, + k8s_service_account_token_provider as k8s_service_account_token_provider, + azure_managed_identity_token_provider as azure_managed_identity_token_provider, +) + +__all__ = [ + "SubjectTokenProvider", + "WorkloadIdentity", + "WorkloadIdentityAuth", + "k8s_service_account_token_provider", + "azure_managed_identity_token_provider", + "gcp_id_token_provider", +] diff --git a/src/openai/auth/_workload.py b/src/openai/auth/_workload.py new file mode 100644 index 0000000000..e3f6f7fb75 --- /dev/null +++ b/src/openai/auth/_workload.py @@ -0,0 +1,313 @@ +from __future__ import annotations + +import time +import threading +from typing import Any, Callable, TypedDict, cast +from pathlib import Path +from typing_extensions import Literal, NotRequired + +import httpx + +from .._exceptions import OAuthError, OpenAIError, SubjectTokenProviderError +from .._utils._sync import to_thread + +TOKEN_EXCHANGE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange" +DEFAULT_TOKEN_EXCHANGE_URL = "https://auth.openai.com/oauth/token" +DEFAULT_REFRESH_BUFFER_SECONDS = 1200 + +SUBJECT_TOKEN_TYPES = { + "jwt": "urn:ietf:params:oauth:token-type:jwt", + "id": "urn:ietf:params:oauth:token-type:id_token", +} + + +class SubjectTokenProvider(TypedDict): + token_type: Literal["jwt", "id"] + get_token: Callable[[], str] + + +class WorkloadIdentity(TypedDict): + """A unique string that identifies the client.""" + + client_id: str + + """Identity provider resource id in WIFAPI.""" + identity_provider_id: str + + """Service account id to bind the verified external identity to.""" + service_account_id: str + + """The provider configuration for obtaining the subject token.""" + provider: SubjectTokenProvider + + """Optional buffer time in seconds to refresh the OpenAI token before it expires. Defaults to 1200 seconds (20 minutes).""" + refresh_buffer_seconds: NotRequired[float] + + +def k8s_service_account_token_provider( + token_file_path: str | Path = "/var/run/secrets/kubernetes.io/serviceaccount/token", +) -> SubjectTokenProvider: + """ + Get a subject token provider for Kubernetes clusters with Workload Identity configured. + + Cloud providers typically mount the subject token as a file in the container. + + Args: + token_file_path: path to the mounted service account token file. Defaults to `/var/run/secrets/kubernetes.io/serviceaccount/token`. + """ + + def get_token() -> str: + try: + with open(token_file_path, "r") as f: + token = f.read().strip() + if not token: + raise SubjectTokenProviderError(f"The token file at {token_file_path} is empty.") + return token + except Exception as e: + raise SubjectTokenProviderError(f"Failed to read the token file at {token_file_path}: {e}") from e + + return {"token_type": "jwt", "get_token": get_token} + + +def azure_managed_identity_token_provider( + resource: str = "https://management.azure.com/", + *, + object_id: str | None = None, + client_id: str | None = None, + msi_res_id: str | None = None, + api_version: str = "2018-02-01", + timeout: float = 10.0, + http_client: httpx.Client | None = None, +) -> SubjectTokenProvider: + """ + Get a subject token provider for Azure Managed Identities. + + See: https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http + + Args: + resource: the resource URI to request a token for. Defaults to `https://management.azure.com/` (Azure Resource Manager). + object_id: the object ID of the managed identity to use, when multiple are assigned. + client_id: the client ID of the managed identity to use, when multiple are assigned. + msi_res_id: the ARM resource ID of the managed identity to use, when multiple are assigned. + api_version: the Azure IMDS API version. Defaults to `2018-02-01`. + timeout: the request timeout in seconds. Defaults to 10.0. + http_client: optional httpx.Client instance to use for requests. If not provided, a new client will be created for each request. + """ + + def get_token() -> str: + try: + url = "http://169.254.169.254/metadata/identity/oauth2/token" + params: dict[str, str] = {"api-version": api_version, "resource": resource} + if object_id is not None: + params["object_id"] = object_id + if client_id is not None: + params["client_id"] = client_id + if msi_res_id is not None: + params["msi_res_id"] = msi_res_id + + if http_client is not None: + response = http_client.get(url, params=params, headers={"Metadata": "true"}, timeout=timeout) + else: + with httpx.Client() as client: + response = client.get(url, params=params, headers={"Metadata": "true"}, timeout=timeout) + + if response.is_error: + raise SubjectTokenProviderError( + f"Failed to fetch Azure subject token from IMDS: HTTP {response.status_code}", + response=response, + ) + data = response.json() + token = data.get("access_token") + if not token: + raise SubjectTokenProviderError( + "Azure IMDS response did not include an access_token", response=response + ) + return cast(str, token) + except Exception as e: + raise SubjectTokenProviderError(f"Failed to fetch Azure subject token from IMDS: {e}") from e + + return {"token_type": "jwt", "get_token": get_token} + + +def gcp_id_token_provider( + audience: str = "https://api.openai.com/v1", + *, + timeout: float = 10.0, + http_client: httpx.Client | None = None, +) -> SubjectTokenProvider: + """ + Get a subject token provider for GCP VM instances using the instance metadata server. + + See: https://cloud.google.com/compute/docs/instances/verifying-instance-identity + + Args: + audience: the unique URI agreed upon by both the instance and the system verifying + the instance's identity. Defaults to `https://api.openai.com/v1`. + timeout: the request timeout in seconds. Defaults to 10.0. + http_client: optional httpx.Client instance to use for requests. If not provided, a new client will be created for each request. + """ + + def get_token() -> str: + try: + url = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity" + params = {"audience": audience} + + if http_client is not None: + response = http_client.get(url, params=params, headers={"Metadata-Flavor": "Google"}, timeout=timeout) + else: + with httpx.Client() as client: + response = client.get(url, params=params, headers={"Metadata-Flavor": "Google"}, timeout=timeout) + + if response.is_error: + raise SubjectTokenProviderError( + f"Failed to fetch GCP subject token from metadata server: HTTP {response.status_code}", + response=response, + ) + token = response.text.strip() + if not token: + raise SubjectTokenProviderError("GCP metadata server returned an empty token", response=response) + return token + except Exception as e: + raise SubjectTokenProviderError(f"Failed to fetch GCP subject token from metadata server: {e}") from e + + return {"token_type": "id", "get_token": get_token} + + +class WorkloadIdentityAuth: + def __init__( + self, + *, + workload_identity: WorkloadIdentity, + token_exchange_url: str = DEFAULT_TOKEN_EXCHANGE_URL, + ): + self.workload_identity = workload_identity + self.token_exchange_url = token_exchange_url + + self._cached_token: str | None = None + self._cached_token_expires_at_monotonic: float | None = None + self._cached_token_refresh_at_monotonic: float | None = None + self._refreshing: bool = False + self._lock = threading.Lock() + self._condition = threading.Condition(self._lock) + + def get_token(self) -> str: + with self._lock: + while self._refreshing and self._token_unusable(): + self._condition.wait() + + if not self._token_unusable() and not self._needs_refresh(): + return cast(str, self._cached_token) + + if self._refreshing: + while self._refreshing: + self._condition.wait() + token = self._cached_token # type: ignore[unreachable] + if self._token_unusable(): + raise RuntimeError("Token is unusable after refresh completed") + return cast(str, token) + + self._refreshing = True + + try: + self._perform_refresh() + with self._lock: + if self._token_unusable(): + raise RuntimeError("Token is unusable after refresh completed") + return cast(str, self._cached_token) + finally: + with self._lock: + self._refreshing = False + self._condition.notify_all() + + async def get_token_async(self) -> str: + return await to_thread(self.get_token) + + def invalidate_token(self) -> None: + with self._lock: + self._cached_token = None + self._cached_token_expires_at_monotonic = None + self._cached_token_refresh_at_monotonic = None + + def _perform_refresh(self) -> None: + token_data = self._fetch_token_from_exchange() + now = time.monotonic() + expires_in = token_data["expires_in"] + + with self._lock: + self._cached_token = token_data["access_token"] + self._cached_token_expires_at_monotonic = now + expires_in + self._cached_token_refresh_at_monotonic = now + self._refresh_delay_seconds(expires_in) + + def _fetch_token_from_exchange(self) -> dict[str, Any]: + subject_token = self._get_subject_token() + + token_type = self.workload_identity["provider"]["token_type"] + subject_token_type = SUBJECT_TOKEN_TYPES.get(token_type) + if subject_token_type is None: + raise OpenAIError( + f"Unsupported token type: {token_type!r}. Supported types: {', '.join(SUBJECT_TOKEN_TYPES.keys())}" + ) + + with httpx.Client() as client: + response = client.post( + self.token_exchange_url, + json={ + "grant_type": TOKEN_EXCHANGE_GRANT_TYPE, + "client_id": self.workload_identity["client_id"], + "subject_token": subject_token, + "subject_token_type": subject_token_type, + "identity_provider_id": self.workload_identity["identity_provider_id"], + "service_account_id": self.workload_identity["service_account_id"], + }, + timeout=10.0, + ) + return self._handle_token_response(response) + + def _handle_token_response(self, response: httpx.Response) -> dict[str, Any]: + try: + body = response.json() if response.content else None + except ValueError: + body = None + + if response.status_code in (400, 401, 403): + raise OAuthError(response=response, body=body) + + if response.is_success: + if body is None: + raise OpenAIError("Token exchange succeeded but response body was empty") + access_token = body.get("access_token") + expires_in = body.get("expires_in") + if not isinstance(access_token, str) or not access_token: + raise OpenAIError("Token exchange response did not include a valid access_token") + if not isinstance(expires_in, (int, float)): + raise OpenAIError("Token exchange response did not include a valid expires_in") + return {"access_token": access_token, "expires_in": float(expires_in)} + + raise OpenAIError( + f"Token exchange failed with status {response.status_code}", + ) + + def _get_subject_token(self) -> str: + provider = self.workload_identity["provider"] + subject_token = provider["get_token"]() + if not subject_token: + raise OpenAIError("The workload identity provider returned an empty subject token") + return subject_token + + def _token_unusable(self) -> bool: + return self._cached_token is None or self._token_expired() + + def _token_expired(self) -> bool: + if self._cached_token_expires_at_monotonic is None: + return True + return time.monotonic() >= self._cached_token_expires_at_monotonic + + def _needs_refresh(self) -> bool: + if self._cached_token_refresh_at_monotonic is None: + return False + return time.monotonic() >= self._cached_token_refresh_at_monotonic + + def _refresh_delay_seconds(self, expires_in: float) -> float: + configured_buffer = self.workload_identity.get("refresh_buffer_seconds", DEFAULT_REFRESH_BUFFER_SECONDS) + effective_buffer = min(configured_buffer, expires_in / 2) + return max(expires_in - effective_buffer, 0.0) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index ad64707261..09fdd9507e 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -7,6 +7,7 @@ import httpx +from ..auth import WorkloadIdentity from .._types import NOT_GIVEN, Omit, Query, Timeout, NotGiven from .._utils import is_given, is_mapping from .._client import OpenAI, AsyncOpenAI @@ -155,6 +156,8 @@ def __init__( azure_endpoint: str | None = None, azure_deployment: str | None = None, api_key: str | Callable[[], str] | None = None, + # workload_identity is not functional in the Azure client + workload_identity: WorkloadIdentity | None = None, # noqa: ARG002 azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -259,6 +262,7 @@ def copy( self, *, api_key: str | Callable[[], str] | None = None, + workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -281,6 +285,7 @@ def copy( """ return super().copy( api_key=api_key, + workload_identity=workload_identity, organization=organization, project=project, webhook_secret=webhook_secret, @@ -436,6 +441,8 @@ def __init__( azure_deployment: str | None = None, api_version: str | None = None, api_key: str | Callable[[], Awaitable[str]] | None = None, + # workload_identity is not functional in the Azure client + workload_identity: WorkloadIdentity | None = None, # noqa: ARG002 azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -540,6 +547,7 @@ def copy( self, *, api_key: str | Callable[[], Awaitable[str]] | None = None, + workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -562,6 +570,7 @@ def copy( """ return super().copy( api_key=api_key, + workload_identity=workload_identity, organization=organization, project=project, webhook_secret=webhook_secret, diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index d8dbea71ad..6074eb0a1d 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -14,6 +14,7 @@ Reasoning as Reasoning, ErrorObject as ErrorObject, CompoundFilter as CompoundFilter, + OAuthErrorCode as OAuthErrorCode, ResponsesModel as ResponsesModel, ReasoningEffort as ReasoningEffort, ComparisonFilter as ComparisonFilter, diff --git a/src/openai/types/shared/__init__.py b/src/openai/types/shared/__init__.py index 2930b9ae3b..55d3e86371 100644 --- a/src/openai/types/shared/__init__.py +++ b/src/openai/types/shared/__init__.py @@ -7,6 +7,7 @@ from .error_object import ErrorObject as ErrorObject from .compound_filter import CompoundFilter as CompoundFilter from .responses_model import ResponsesModel as ResponsesModel +from .oauth_error_code import OAuthErrorCode as OAuthErrorCode from .reasoning_effort import ReasoningEffort as ReasoningEffort from .comparison_filter import ComparisonFilter as ComparisonFilter from .function_definition import FunctionDefinition as FunctionDefinition diff --git a/src/openai/types/shared/oauth_error_code.py b/src/openai/types/shared/oauth_error_code.py new file mode 100644 index 0000000000..4ef3924293 --- /dev/null +++ b/src/openai/types/shared/oauth_error_code.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["OAuthErrorCode"] + +OAuthErrorCode: TypeAlias = Union[Literal["invalid_grant", "invalid_subject_token"], str] diff --git a/src/openai/types/shared_params/__init__.py b/src/openai/types/shared_params/__init__.py index b6c0912b0f..0ed5fbaa80 100644 --- a/src/openai/types/shared_params/__init__.py +++ b/src/openai/types/shared_params/__init__.py @@ -5,6 +5,7 @@ from .chat_model import ChatModel as ChatModel from .compound_filter import CompoundFilter as CompoundFilter from .responses_model import ResponsesModel as ResponsesModel +from .oauth_error_code import OAuthErrorCode as OAuthErrorCode from .reasoning_effort import ReasoningEffort as ReasoningEffort from .comparison_filter import ComparisonFilter as ComparisonFilter from .function_definition import FunctionDefinition as FunctionDefinition diff --git a/src/openai/types/shared_params/oauth_error_code.py b/src/openai/types/shared_params/oauth_error_code.py new file mode 100644 index 0000000000..cb3c981881 --- /dev/null +++ b/src/openai/types/shared_params/oauth_error_code.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, TypeAlias + +__all__ = ["OAuthErrorCode"] + +OAuthErrorCode: TypeAlias = Union[Literal["invalid_grant", "invalid_subject_token"], str] diff --git a/tests/test_auth.py b/tests/test_auth.py new file mode 100644 index 0000000000..c8f4f2d7cf --- /dev/null +++ b/tests/test_auth.py @@ -0,0 +1,190 @@ +import json +from typing import cast +from pathlib import Path + +import httpx +import respx +import pytest +from respx.models import Call +from inline_snapshot import snapshot + +from openai import OpenAI, OAuthError +from openai.auth._workload import ( + gcp_id_token_provider, + k8s_service_account_token_provider, + azure_managed_identity_token_provider, +) + + +@respx.mock +def test_basic_auth(): + respx.post("https://auth.openai.com/oauth/token").mock( + return_value=httpx.Response( + 200, + json={ + "access_token": "fake_access_token", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ) + ) + + respx.get("https://api.openai.com/v1/models").mock( + return_value=httpx.Response(200, json={"data": [], "object": "list"}) + ) + + client = OpenAI( + workload_identity={ + "client_id": "client_123", + "identity_provider_id": "idp_123", + "service_account_id": "sa_123", + "provider": { + "get_token": lambda: "fake_subject_token", + "token_type": "jwt", + }, + }, + ) + + client.models.list() + + assert len(respx.calls) == 2 + token_call = cast(Call, respx.calls[0]) + api_call = cast(Call, respx.calls[1]) + + assert token_call.request.url == "https://auth.openai.com/oauth/token" + assert api_call.request.headers.get("Authorization") == "Bearer fake_access_token" + + +@respx.mock +def test_workload_identity_exchange_payload_and_cache() -> None: + provider_call_count = 0 + + def provider() -> str: + nonlocal provider_call_count + provider_call_count += 1 + return "fake_subject_token" + + exchange_route = respx.post("https://auth.openai.com/oauth/token").mock( + return_value=httpx.Response( + 200, + json={ + "access_token": "fake_access_token", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ) + ) + api_route = respx.get("https://api.openai.com/v1/models").mock( + return_value=httpx.Response(200, json={"data": [], "object": "list"}) + ) + + client = OpenAI( + workload_identity={ + "client_id": "client_123", + "identity_provider_id": "idp_123", + "service_account_id": "sa_123", + "provider": { + "get_token": provider, + "token_type": "jwt", + }, + }, + ) + + client.models.list() + client.models.list() + + assert provider_call_count == 1 + assert exchange_route.call_count == 1 + assert api_route.call_count == 2 + + exchange_request = cast(respx.models.Call, exchange_route.calls[0]).request + assert json.loads(exchange_request.content) == snapshot( + { + "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", + "client_id": "client_123", + "subject_token": "fake_subject_token", + "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", + "identity_provider_id": "idp_123", + "service_account_id": "sa_123", + } + ) + + assert ( + cast(respx.models.Call, api_route.calls[0]).request.headers.get("Authorization") == "Bearer fake_access_token" + ) + assert ( + cast(respx.models.Call, api_route.calls[1]).request.headers.get("Authorization") == "Bearer fake_access_token" + ) + + +@respx.mock +def test_workload_identity_exchange_error() -> None: + exchange_route = respx.post("https://auth.openai.com/oauth/token").mock( + return_value=httpx.Response( + 401, + json={ + "error": "invalid_grant", + "error_description": "No service account mapping found for the provided service_account_id.", + }, + ) + ) + api_route = respx.get("https://api.openai.com/v1/models").mock( + return_value=httpx.Response(200, json={"data": [], "object": "list"}) + ) + + client = OpenAI( + workload_identity={ + "client_id": "client_123", + "identity_provider_id": "idp_123", + "service_account_id": "sa_123", + "provider": { + "get_token": lambda: "fake_subject_token", + "token_type": "jwt", + }, + }, + ) + + with pytest.raises(OAuthError) as exc: + client.models.list() + + assert exc.value.message == "No service account mapping found for the provided service_account_id." + assert exc.value.error == "invalid_grant" + assert exc.value.status_code == 401 + assert exchange_route.call_count == 1 + assert api_route.call_count == 0 + + +def test_k8s_service_account_token_provider(tmp_path: Path) -> None: + token_file = tmp_path / "token" + token_file.write_text("my-k8s-token") + + provider = k8s_service_account_token_provider(token_file) + + assert provider["token_type"] == "jwt" + assert provider["get_token"]() == "my-k8s-token" + + +@respx.mock +def test_azure_managed_identity_token_provider() -> None: + respx.get("http://169.254.169.254/metadata/identity/oauth2/token").mock( + return_value=httpx.Response(200, json={"access_token": "azure-token"}) + ) + + provider = azure_managed_identity_token_provider() + + assert provider["token_type"] == "jwt" + assert provider["get_token"]() == "azure-token" + + +@respx.mock +def test_gcp_id_token_provider() -> None: + respx.get("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity").mock( + return_value=httpx.Response(200, text="gcp-token") + ) + + provider = gcp_id_token_provider() + + assert provider["token_type"] == "id" + assert provider["get_token"]() == "gcp-token" diff --git a/tests/test_client.py b/tests/test_client.py index 04ef6794ed..570042c46a 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -20,6 +20,7 @@ from pydantic import ValidationError from openai import OpenAI, AsyncOpenAI, APIResponseValidationError +from openai.auth import WorkloadIdentity from openai._types import Omit from openai._utils import asyncify from openai._models import BaseModel, FinalRequestOptions @@ -41,6 +42,15 @@ T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" +workload_identity: WorkloadIdentity = { + "client_id": "client-id", + "identity_provider_id": "identity-provider-id", + "service_account_id": "service-account-id", + "provider": { + "token_type": "jwt", + "get_token": lambda: "external-subject-token", + }, +} class MockRequestCall(Protocol): @@ -414,6 +424,19 @@ def test_validate_headers(self) -> None: client2 = OpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) _ = client2 + def test_workload_identity_is_mutually_exclusive_with_api_key(self) -> None: + with pytest.raises( + OpenAIError, + match="The `api_key` and `workload_identity` arguments are mutually exclusive", + ): + OpenAI( + base_url=base_url, + api_key=api_key, + workload_identity=workload_identity, # type: ignore[reportArgumentType] + organization="org_123", + _strict_response_validation=True, + ) + def test_default_query_option(self) -> None: client = OpenAI( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} @@ -2189,7 +2212,6 @@ async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_cli assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" - @pytest.mark.asyncio async def test_api_key_before_after_refresh_provider(self) -> None: async def mock_api_key_provider(): return "test_bearer_token" @@ -2204,7 +2226,6 @@ async def mock_api_key_provider(): assert client.api_key == "test_bearer_token" assert client.auth_headers.get("Authorization") == "Bearer test_bearer_token" - @pytest.mark.asyncio async def test_api_key_before_after_refresh_str(self) -> None: client = AsyncOpenAI(base_url=base_url, api_key="test_api_key") @@ -2213,7 +2234,6 @@ async def test_api_key_before_after_refresh_str(self) -> None: assert client.auth_headers.get("Authorization") == "Bearer test_api_key" - @pytest.mark.asyncio @pytest.mark.respx() async def test_bearer_token_refresh_async(self, respx_mock: MockRouter) -> None: respx_mock.post(base_url + "/chat/completions").mock( @@ -2244,7 +2264,6 @@ async def token_provider() -> str: assert calls[0].request.headers.get("Authorization") == "Bearer first" assert calls[1].request.headers.get("Authorization") == "Bearer second" - @pytest.mark.asyncio async def test_copy_auth(self) -> None: async def token_provider_1() -> str: return "test_bearer_token_1" @@ -2255,3 +2274,299 @@ async def token_provider_2() -> str: client = AsyncOpenAI(base_url=base_url, api_key=token_provider_1).copy(api_key=token_provider_2) await client._refresh_api_key() assert client.auth_headers == {"Authorization": "Bearer test_bearer_token_2"} + + +class TestWorkloadIdentity401Retry: + @pytest.mark.respx() + def test_workload_identity_401_retry(self, respx_mock: MockRouter) -> None: + provider_call_count = 0 + + def provider() -> str: + nonlocal provider_call_count + provider_call_count += 1 + return f"external-subject-token-{provider_call_count}" + + respx_mock.post("https://auth.openai.com/oauth/token").mock( + side_effect=[ + httpx.Response( + 200, + json={ + "access_token": "openai-access-token-1", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ), + httpx.Response( + 200, + json={ + "access_token": "openai-access-token-2", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ), + ] + ) + + respx_mock.post(base_url + "/chat/completions").mock( + side_effect=[ + httpx.Response(401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}), + httpx.Response( + 200, + json={ + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1234567890, + "model": "gpt-4", + "choices": [], + }, + ), + ] + ) + + with OpenAI( + base_url=base_url, + workload_identity={ + **workload_identity, + "provider": { + "get_token": provider, + "token_type": "jwt", + }, + }, + organization="org_123", + project="proj_123", + _strict_response_validation=True, + ) as client: + client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 4 + + assert calls[0].request.url == httpx.URL("https://auth.openai.com/oauth/token") + assert calls[1].request.url == httpx.URL(base_url + "/chat/completions") + assert calls[1].request.headers.get("Authorization") == "Bearer openai-access-token-1" + + assert calls[2].request.url == httpx.URL("https://auth.openai.com/oauth/token") + + assert calls[3].request.url == httpx.URL(base_url + "/chat/completions") + assert calls[3].request.headers.get("Authorization") == "Bearer openai-access-token-2" + + assert provider_call_count == 2 + + @pytest.mark.respx() + def test_401_without_workload_identity_no_retry(self, respx_mock: MockRouter) -> None: + respx_mock.post(base_url + "/chat/completions").mock( + return_value=httpx.Response( + 401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}} + ) + ) + + with OpenAI( + base_url=base_url, + api_key="test-api-key", + _strict_response_validation=True, + ) as client: + with pytest.raises(APIStatusError) as exc_info: + client.chat.completions.create(messages=[], model="gpt-4") + + assert exc_info.value.status_code == 401 + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 1 + + @pytest.mark.respx() + def test_non_401_errors_no_retry(self, respx_mock: MockRouter) -> None: + provider_call_count = 0 + + def provider() -> str: + nonlocal provider_call_count + provider_call_count += 1 + return "external-subject-token" + + respx_mock.post("https://auth.openai.com/oauth/token").mock( + return_value=httpx.Response( + 200, + json={ + "access_token": "openai-access-token-1", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ) + ) + + respx_mock.post(base_url + "/chat/completions").mock( + return_value=httpx.Response(403, json={"error": {"message": "Forbidden", "type": "invalid_request_error"}}) + ) + + with OpenAI( + base_url=base_url, + workload_identity={ + **workload_identity, + "provider": { + "get_token": provider, + "token_type": "jwt", + }, + }, + organization="org_123", + project="proj_123", + _strict_response_validation=True, + ) as client: + with pytest.raises(APIStatusError) as exc_info: + client.chat.completions.create(messages=[], model="gpt-4") + + assert exc_info.value.status_code == 403 + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 2 + + assert provider_call_count == 1 + + +class TestAsyncWorkloadIdentity401Retry: + @pytest.mark.respx() + async def test_workload_identity_401_retry(self, respx_mock: MockRouter) -> None: + provider_call_count = 0 + + def provider() -> str: + nonlocal provider_call_count + provider_call_count += 1 + return f"external-subject-token-{provider_call_count}" + + respx_mock.post("https://auth.openai.com/oauth/token").mock( + side_effect=[ + httpx.Response( + 200, + json={ + "access_token": "openai-access-token-1", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ), + httpx.Response( + 200, + json={ + "access_token": "openai-access-token-2", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ), + ] + ) + + respx_mock.post(base_url + "/chat/completions").mock( + side_effect=[ + httpx.Response(401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}}), + httpx.Response( + 200, + json={ + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1234567890, + "model": "gpt-4", + "choices": [], + }, + ), + ] + ) + + async with AsyncOpenAI( + base_url=base_url, + workload_identity={ + **workload_identity, + "provider": { + "get_token": provider, + "token_type": "jwt", + }, + }, + organization="org_123", + project="proj_123", + _strict_response_validation=True, + ) as client: + await client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 4 + + assert calls[0].request.url == httpx.URL("https://auth.openai.com/oauth/token") + assert calls[1].request.url == httpx.URL(base_url + "/chat/completions") + assert calls[1].request.headers.get("Authorization") == "Bearer openai-access-token-1" + + assert calls[2].request.url == httpx.URL("https://auth.openai.com/oauth/token") + + assert calls[3].request.url == httpx.URL(base_url + "/chat/completions") + assert calls[3].request.headers.get("Authorization") == "Bearer openai-access-token-2" + + assert provider_call_count == 2 + + @pytest.mark.respx() + async def test_401_without_workload_identity_no_retry(self, respx_mock: MockRouter) -> None: + respx_mock.post(base_url + "/chat/completions").mock( + return_value=httpx.Response( + 401, json={"error": {"message": "Unauthorized", "type": "invalid_request_error"}} + ) + ) + + async with AsyncOpenAI( + base_url=base_url, + api_key="test-api-key", + _strict_response_validation=True, + ) as client: + with pytest.raises(APIStatusError) as exc_info: + await client.chat.completions.create(messages=[], model="gpt-4") + + assert exc_info.value.status_code == 401 + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 1 + + @pytest.mark.respx() + async def test_non_401_errors_no_retry(self, respx_mock: MockRouter) -> None: + provider_call_count = 0 + + def provider() -> str: + nonlocal provider_call_count + provider_call_count += 1 + return "external-subject-token" + + respx_mock.post("https://auth.openai.com/oauth/token").mock( + return_value=httpx.Response( + 200, + json={ + "access_token": "openai-access-token-1", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "token_type": "Bearer", + "expires_in": 3600, + }, + ) + ) + + respx_mock.post(base_url + "/chat/completions").mock( + return_value=httpx.Response(403, json={"error": {"message": "Forbidden", "type": "invalid_request_error"}}) + ) + + async with AsyncOpenAI( + base_url=base_url, + workload_identity={ + **workload_identity, + "provider": { + "get_token": provider, + "token_type": "jwt", + }, + }, + organization="org_123", + project="proj_123", + _strict_response_validation=True, + ) as client: + with pytest.raises(APIStatusError) as exc_info: + await client.chat.completions.create(messages=[], model="gpt-4") + + assert exc_info.value.status_code == 403 + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert len(calls) == 2 + + assert provider_call_count == 1 From 750354ed65565b31d0547bf00f4f3180ac1bfeef Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 19:57:59 +0000 Subject: [PATCH 293/408] release: 2.31.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ef8743735a..8f5c652f67 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.30.0" + ".": "2.31.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 785fab5782..bf8092f51f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## 2.31.0 (2026-04-08) + +Full Changelog: [v2.30.0...v2.31.0](https://github.com/openai/openai-python/compare/v2.30.0...v2.31.0) + +### Features + +* **api:** add phase field to conversations message ([3e5834e](https://github.com/openai/openai-python/commit/3e5834efb39b24e019a29dc54d890c67d18cbb54)) +* **api:** add web_search_call.results to ResponseIncludable type ([ffd8741](https://github.com/openai/openai-python/commit/ffd8741dd38609a5af0159ceb800d8ddba7925f8)) +* **client:** add support for short-lived tokens ([#1608](https://github.com/openai/openai-python/issues/1608)) ([22fe722](https://github.com/openai/openai-python/commit/22fe7228d4990c197cd721b3ad7931ad05cca5dd)) +* **client:** support sending raw data over websockets ([f1bc52e](https://github.com/openai/openai-python/commit/f1bc52ef641dfca6fdf2a5b00ce3b09bff2552f5)) +* **internal:** implement indices array format for query and form serialization ([49194cf](https://github.com/openai/openai-python/commit/49194cfa711328216ff131d6f65c9298822a7c51)) + + +### Bug Fixes + +* **client:** preserve hardcoded query params when merging with user params ([92e109c](https://github.com/openai/openai-python/commit/92e109c3d9569a942e1919e75977dc13fa015f9a)) +* **types:** remove web_search_call.results from ResponseIncludable ([d3cc401](https://github.com/openai/openai-python/commit/d3cc40165cd86015833d15167cc7712b4102f932)) + + +### Chores + +* **tests:** bump steady to v0.20.1 ([d60e2ee](https://github.com/openai/openai-python/commit/d60e2eea7f6916540cd4ba901dceb07051119da4)) +* **tests:** bump steady to v0.20.2 ([6508d47](https://github.com/openai/openai-python/commit/6508d474332d4e82d9615c0a9a77379f9b5e4412)) + + +### Documentation + +* **api:** update file parameter descriptions in vector_stores files and file_batches ([a9e7ebd](https://github.com/openai/openai-python/commit/a9e7ebd505b9ae90514339aa63c6f1984a08cf6b)) + ## 2.30.0 (2026-03-25) Full Changelog: [v2.29.0...v2.30.0](https://github.com/openai/openai-python/compare/v2.29.0...v2.30.0) diff --git a/pyproject.toml b/pyproject.toml index bfc0e13be7..c85db6b519 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.30.0" +version = "2.31.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 788e82e056..a435bdb765 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.30.0" # x-release-please-version +__version__ = "2.31.0" # x-release-please-version From e507a4ebeea4c3f93cd48986014a3e2ca79230c2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 18:27:17 -0400 Subject: [PATCH 294/408] release: 2.32.0 (#3074) --- .github/workflows/ci.yml | 48 +- .github/workflows/create-releases.yml | 12 +- .github/workflows/detect-breaking-changes.yml | 27 +- .github/workflows/publish-pypi.yml | 12 +- .release-please-manifest.json | 2 +- .stats.yml | 6 +- CHANGELOG.md | 22 + pyproject.toml | 2 +- src/openai/__init__.py | 7 + src/openai/_event_handler.py | 85 +++ src/openai/_exceptions.py | 18 + src/openai/_send_queue.py | 90 +++ src/openai/_utils/_utils.py | 5 +- src/openai/_version.py | 2 +- src/openai/resources/realtime/realtime.py | 694 +++++++++++++++-- src/openai/resources/responses/responses.py | 707 ++++++++++++++++-- src/openai/types/__init__.py | 4 + .../types/responses/response_input_file.py | 7 + .../responses/response_input_file_content.py | 7 + .../response_input_file_content_param.py | 7 + .../responses/response_input_file_param.py | 7 + src/openai/types/websocket_reconnection.py | 64 ++ tests/api_resources/audio/test_speech.py | 32 +- tests/api_resources/beta/test_assistants.py | 4 +- tests/api_resources/beta/test_threads.py | 8 +- tests/api_resources/beta/threads/test_runs.py | 8 +- tests/api_resources/chat/test_completions.py | 8 +- tests/api_resources/realtime/test_calls.py | 32 +- .../realtime/test_client_secrets.py | 16 +- tests/api_resources/test_completions.py | 32 +- tests/api_resources/test_images.py | 20 +- tests/api_resources/test_moderations.py | 4 +- tests/api_resources/test_responses.py | 8 +- tests/api_resources/test_videos.py | 4 +- tests/test_extract_files.py | 9 + tests/test_send_queue.py | 128 ++++ 36 files changed, 1888 insertions(+), 260 deletions(-) create mode 100644 src/openai/_event_handler.py create mode 100644 src/openai/_send_queue.py create mode 100644 src/openai/types/websocket_reconnection.py create mode 100644 tests/test_send_queue.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 414f8ce856..527e036a0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,13 +23,11 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true - name: Install dependencies run: rye sync --all-features @@ -48,13 +46,11 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true - name: Install dependencies run: rye sync --all-features @@ -89,13 +85,11 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true - name: Bootstrap run: ./scripts/bootstrap @@ -112,13 +106,11 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true - name: Install dependencies run: | rye sync --all-features diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 98b7f20ffe..f819d07310 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -22,14 +22,12 @@ jobs: repo: ${{ github.event.repository.full_name }} stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} - - name: Install Rye + - name: Set up Rye if: ${{ steps.release.outputs.releases_created }} - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true - name: Publish to PyPI if: ${{ steps.release.outputs.releases_created }} diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index bd73bb6e2a..641129f010 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -20,13 +20,11 @@ jobs: # Ensure we can check out the pull request base in the script below. fetch-depth: ${{ env.FETCH_DEPTH }} - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true - name: Install dependencies run: | rye sync --all-features @@ -49,14 +47,12 @@ jobs: with: path: openai-python - - name: Install Rye - working-directory: openai-python - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true + working-directory: openai-python - name: Install dependencies working-directory: openai-python @@ -85,4 +81,3 @@ jobs: - name: Run integration type checks working-directory: openai-agents-python run: make mypy - diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index f67347f2fa..a7c62c4c4d 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -13,13 +13,11 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Install Rye - run: | - curl -sSf https://rye.astral.sh/get | bash - echo "$HOME/.rye/shims" >> $GITHUB_PATH - env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true - name: Publish to PyPI run: | diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8f5c652f67..527d2e30b0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.31.0" + ".": "2.32.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 461d4c7c07..f069d6c8b9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a6eca1bd01e0c434af356fe5275c206057216a4e626d1051d294c27016cd6d05.yml -openapi_spec_hash: 68abda9122013a9ae3f084cfdbe8e8c1 -config_hash: 4975e16a94e8f9901428022044131888 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7c540cce6eb30401259f4831ea9803b6d88501605d13734f98212cbb3b199e10.yml +openapi_spec_hash: 06e656be22bbb92689954253668b42fc +config_hash: 1a88b104658b6c854117996c080ebe6b diff --git a/CHANGELOG.md b/CHANGELOG.md index bf8092f51f..2640bc3d63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 2.32.0 (2026-04-15) + +Full Changelog: [v2.31.0...v2.32.0](https://github.com/openai/openai-python/compare/v2.31.0...v2.32.0) + +### Features + +* **api:** Add detail to InputFileContent ([60de21d](https://github.com/openai/openai-python/commit/60de21d1fcfbcadea0d9b8d884c73c9dc49d14ff)) +* **api:** add OAuthErrorCode type ([0c8d2c3](https://github.com/openai/openai-python/commit/0c8d2c3b44242c9139dc554896ea489b56e236b8)) +* **client:** add event handler implementation for websockets ([0280d05](https://github.com/openai/openai-python/commit/0280d0568f706684ecbf0aabf3575cdcb7fd22d5)) +* **client:** allow enqueuing to websockets even when not connected ([67aa20e](https://github.com/openai/openai-python/commit/67aa20e69bc0e4a3b7694327c808606bfa24a966)) +* **client:** support reconnection in websockets ([eb72a95](https://github.com/openai/openai-python/commit/eb72a953ea9dc5beec3eef537be6eb32292c3f65)) + + +### Bug Fixes + +* ensure file data are only sent as 1 parameter ([c0c2ecd](https://github.com/openai/openai-python/commit/c0c2ecd0f6b64fa5fafda6134bb06995b143a2cf)) + + +### Documentation + +* improve examples ([84712fa](https://github.com/openai/openai-python/commit/84712fa0f094b53151a0fe6ac85aa98018b2a7e2)) + ## 2.31.0 (2026-04-08) Full Changelog: [v2.30.0...v2.31.0](https://github.com/openai/openai-python/compare/v2.30.0...v2.31.0) diff --git a/pyproject.toml b/pyproject.toml index c85db6b519..d0d533e8a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.31.0" +version = "2.32.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 3e0a135929..fc9675a8b5 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -29,14 +29,17 @@ InternalServerError, PermissionDeniedError, LengthFinishReasonError, + WebSocketQueueFullError, UnprocessableEntityError, APIResponseValidationError, InvalidWebhookSignatureError, ContentFilterFinishReasonError, + WebSocketConnectionClosedError, ) from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging from ._legacy_response import HttpxBinaryResponseContent as HttpxBinaryResponseContent +from .types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides __all__ = [ "types", @@ -84,6 +87,10 @@ "DefaultHttpxClient", "DefaultAsyncHttpxClient", "DefaultAioHttpClient", + "ReconnectingEvent", + "ReconnectingOverrides", + "WebSocketQueueFullError", + "WebSocketConnectionClosedError", ] if not _t.TYPE_CHECKING: diff --git a/src/openai/_event_handler.py b/src/openai/_event_handler.py new file mode 100644 index 0000000000..d9a3a59d71 --- /dev/null +++ b/src/openai/_event_handler.py @@ -0,0 +1,85 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import threading +from typing import Any, Callable + +EventHandler = Callable[..., Any] + + +class EventHandlerRegistry: + """Thread-safe (optional) registry of event handlers.""" + + def __init__(self, *, use_lock: bool = False) -> None: + self._handlers: dict[str, list[EventHandler]] = {} + self._once_ids: set[int] = set() + self._lock: threading.Lock | None = threading.Lock() if use_lock else None + + def _acquire(self) -> None: + if self._lock is not None: + self._lock.acquire() + + def _release(self) -> None: + if self._lock is not None: + self._lock.release() + + def add(self, event_type: str, handler: EventHandler, *, once: bool = False) -> None: + self._acquire() + try: + handlers = self._handlers.setdefault(event_type, []) + handlers.append(handler) + if once: + self._once_ids.add(id(handler)) + finally: + self._release() + + def remove(self, event_type: str, handler: EventHandler) -> None: + self._acquire() + try: + handlers = self._handlers.get(event_type) + if handlers is not None: + try: + handlers.remove(handler) + except ValueError: + pass + self._once_ids.discard(id(handler)) + finally: + self._release() + + def get_handlers(self, event_type: str) -> list[EventHandler]: + """Return a snapshot of handlers for the given event type, removing once-handlers.""" + self._acquire() + try: + handlers = self._handlers.get(event_type) + if not handlers: + return [] + result = list(handlers) + to_remove = [h for h in result if id(h) in self._once_ids] + for h in to_remove: + handlers.remove(h) + self._once_ids.discard(id(h)) + return result + finally: + self._release() + + def has_handlers(self, event_type: str) -> bool: + self._acquire() + try: + handlers = self._handlers.get(event_type) + return bool(handlers) + finally: + self._release() + + def merge_into(self, target: EventHandlerRegistry) -> None: + """Move all handlers from this registry into *target*, then clear self.""" + self._acquire() + try: + for event_type, handlers in self._handlers.items(): + for handler in handlers: + once = id(handler) in self._once_ids + target.add(event_type, handler, once=once) + self._handlers.clear() + self._once_ids.clear() + finally: + self._release() diff --git a/src/openai/_exceptions.py b/src/openai/_exceptions.py index a37ed8ca82..86f44b0e15 100644 --- a/src/openai/_exceptions.py +++ b/src/openai/_exceptions.py @@ -28,6 +28,8 @@ "ContentFilterFinishReasonError", "InvalidWebhookSignatureError", "SubjectTokenProviderError", + "WebSocketConnectionClosedError", + "WebSocketQueueFullError", ] @@ -187,3 +189,19 @@ def __init__(self) -> None: class InvalidWebhookSignatureError(ValueError): """Raised when a webhook signature is invalid, meaning the computed signature does not match the expected signature.""" + + +class WebSocketConnectionClosedError(OpenAIError): + """Raised when a WebSocket connection closes with unsent messages.""" + + unsent_messages: list[str] + + def __init__(self, message: str, *, unsent_messages: list[str]) -> None: + super().__init__(message) + self.unsent_messages = unsent_messages + + +class WebSocketQueueFullError(OpenAIError): + """Raised when the outgoing WebSocket message queue exceeds its byte-size limit.""" + + pass diff --git a/src/openai/_send_queue.py b/src/openai/_send_queue.py new file mode 100644 index 0000000000..b35d0fbcba --- /dev/null +++ b/src/openai/_send_queue.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import typing +import threading + +from ._exceptions import WebSocketQueueFullError + + +class SendQueue: + """Bounded byte-size queue for outgoing WebSocket messages. + + Messages are stored as pre-serialized strings. The queue enforces a + maximum byte budget so that unbounded buffering cannot occur during + reconnection windows. + """ + + def __init__(self, max_bytes: int = 1_048_576) -> None: + self._queue: list[tuple[str, int]] = [] # (data, byte_length) + self._bytes: int = 0 + self._max_bytes = max_bytes + self._lock = threading.Lock() + + def enqueue(self, data: str) -> None: + """Append *data* to the queue. + + Raises :class:`WebSocketQueueFullError` if the message would + exceed the byte-size limit. + """ + byte_length = len(data.encode("utf-8")) + with self._lock: + if self._bytes + byte_length > self._max_bytes: + raise WebSocketQueueFullError("send queue is full, message discarded") + self._queue.append((data, byte_length)) + self._bytes += byte_length + + def flush_sync(self, send: typing.Callable[[str], object]) -> None: + """Send every queued message via *send*. + + If *send* raises, the failing message and all subsequent messages + are re-queued and the error is re-raised. + """ + with self._lock: + pending = list(self._queue) + self._queue.clear() + self._bytes = 0 + + for i, (data, _byte_length) in enumerate(pending): + try: + send(data) + except Exception: + with self._lock: + remaining = pending[i:] + self._queue = remaining + self._queue + self._bytes = sum(bl for _, bl in self._queue) + raise + + async def flush_async(self, send: typing.Callable[[str], typing.Awaitable[object]]) -> None: + """Async variant of :meth:`flush_sync`.""" + with self._lock: + pending = list(self._queue) + self._queue.clear() + self._bytes = 0 + + for i, (data, _byte_length) in enumerate(pending): + try: + await send(data) + except Exception: + with self._lock: + remaining = pending[i:] + self._queue = remaining + self._queue + self._bytes = sum(bl for _, bl in self._queue) + raise + + def drain(self) -> list[str]: + """Remove and return all queued messages.""" + with self._lock: + items = [data for data, _ in self._queue] + self._queue.clear() + self._bytes = 0 + return items + + def __len__(self) -> int: + with self._lock: + return len(self._queue) + + def __bool__(self) -> bool: + with self._lock: + return len(self._queue) > 0 diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index 90494748cc..b1e8e0d041 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -90,8 +90,9 @@ def _extract_items( index += 1 if is_dict(obj): try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]): item = obj.pop(key) else: item = obj[key] diff --git a/src/openai/_version.py b/src/openai/_version.py index a435bdb765..a98695ba0e 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.31.0" # x-release-please-version +__version__ = "2.32.0" # x-release-please-version diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 82c9815b03..e4c5bd8163 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -3,9 +3,11 @@ from __future__ import annotations import json +import time +import random import logging from types import TracebackType -from typing import TYPE_CHECKING, Any, Iterator, cast +from typing import TYPE_CHECKING, Any, Union, Callable, Iterator, Awaitable, cast from typing_extensions import AsyncIterator import httpx @@ -30,7 +32,8 @@ from ..._compat import cached_property from ..._models import construct_type_unchecked from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._exceptions import OpenAIError +from ..._exceptions import OpenAIError, WebSocketConnectionClosedError +from ..._send_queue import SendQueue from ..._base_client import _merge_mappings from .client_secrets import ( ClientSecrets, @@ -40,8 +43,11 @@ ClientSecretsWithStreamingResponse, AsyncClientSecretsWithStreamingResponse, ) +from ..._event_handler import EventHandlerRegistry from ...types.realtime import session_update_event_param +from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close from ...types.websocket_connection_options import WebSocketConnectionOptions +from ...types.realtime.realtime_error_event import RealtimeErrorEvent from ...types.realtime.realtime_client_event import RealtimeClientEvent from ...types.realtime.realtime_server_event import RealtimeServerEvent from ...types.realtime.conversation_item_param import ConversationItemParam @@ -97,6 +103,11 @@ def connect( extra_query: Query = {}, extra_headers: Headers = {}, websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> RealtimeConnectionManager: """ 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. @@ -114,6 +125,11 @@ def connect( extra_query=extra_query, extra_headers=extra_headers, websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, call_id=call_id, model=model, ) @@ -157,6 +173,11 @@ def connect( extra_query: Query = {}, extra_headers: Headers = {}, websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> AsyncRealtimeConnectionManager: """ 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. @@ -174,6 +195,11 @@ def connect( extra_query=extra_query, extra_headers=extra_headers, websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, call_id=call_id, model=model, ) @@ -242,8 +268,31 @@ class AsyncRealtimeConnection: _connection: AsyncWebSocketConnection - def __init__(self, connection: AsyncWebSocketConnection) -> None: + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) self.session = AsyncRealtimeSessionResource(self) self.response = AsyncRealtimeResponseResource(self) @@ -256,13 +305,22 @@ async def __aiter__(self) -> AsyncIterator[RealtimeServerEvent]: An infinite-iterator that will continue to yield events until the connection is closed. """ - from websockets.exceptions import ConnectionClosedOK + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError - try: - while True: + while True: + try: yield await self.recv() - except ConnectionClosedOK: - return + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise async def recv(self) -> RealtimeServerEvent: """ @@ -290,12 +348,24 @@ async def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> N if isinstance(event, BaseModel) else json.dumps(await async_maybe_transform(event, RealtimeClientEventParam)) ) - await self._connection.send(data) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + await self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return await self._connection.send(data) async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True await self._connection.close(code=code, reason=reason) def parse_event(self, data: str | bytes) -> RealtimeServerEvent: @@ -308,6 +378,173 @@ def parse_event(self, data: str | bytes) -> RealtimeServerEvent: RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) ) + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(1, self._max_retries + 1): + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send) + except Exception: + log.warning("Failed to flush send queue after reconnect", exc_info=True) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("conversation.created", my_handler) + + Or as a decorator:: + + @connection.on("conversation.created") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncRealtimeConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, RealtimeErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + class AsyncRealtimeConnectionManager: """ @@ -338,6 +575,11 @@ def __init__( extra_query: Query, extra_headers: Headers, websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> None: self.__client = client self.__call_id = call_id @@ -346,10 +588,66 @@ def __init__( self.__extra_query = extra_query self.__extra_headers = extra_headers self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(event) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncRealtimeConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncRealtimeConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncRealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator async def __aenter__(self) -> AsyncRealtimeConnection: """ - 👋 If your application doesn't work well with the context manager approach then you + If your application doesn't work well with the context manager approach then you can call this method directly to initiate a connection. **Warning**: You must remember to close the connection with `.close()`. @@ -360,15 +658,35 @@ async def __aenter__(self) -> AsyncRealtimeConnection: await connection.close() ``` """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncRealtimeConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: try: from websockets.asyncio.client import connect except ImportError as exc: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc - extra_query = self.__extra_query await self.__client._refresh_api_key() auth_headers = self.__client.auth_headers - extra_query = self.__extra_query if self.__call_id is not omit: extra_query = {**extra_query, "call_id": self.__call_id} if is_async_azure_client(self.__client): @@ -389,24 +707,18 @@ async def __aenter__(self) -> AsyncRealtimeConnection: if self.__websocket_connection_options: log.debug("Connection options: %s", self.__websocket_connection_options) - self.__connection = AsyncRealtimeConnection( - await connect( - str(url), - user_agent_header=self.__client.user_agent, - additional_headers=_merge_mappings( - { - **auth_headers, - }, - self.__extra_headers, - ), - **self.__websocket_connection_options, - ) + return await connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **auth_headers, + }, + extra_headers, + ), + **self.__websocket_connection_options, ) - return self.__connection - - enter = __aenter__ - def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) @@ -436,8 +748,31 @@ class RealtimeConnection: _connection: WebSocketConnection - def __init__(self, connection: WebSocketConnection) -> None: + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) self.session = RealtimeSessionResource(self) self.response = RealtimeResponseResource(self) @@ -450,13 +785,22 @@ def __iter__(self) -> Iterator[RealtimeServerEvent]: An infinite-iterator that will continue to yield events until the connection is closed. """ - from websockets.exceptions import ConnectionClosedOK + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError - try: - while True: + while True: + try: yield self.recv() - except ConnectionClosedOK: - return + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise def recv(self) -> RealtimeServerEvent: """ @@ -484,12 +828,24 @@ def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: if isinstance(event, BaseModel) else json.dumps(maybe_transform(event, RealtimeClientEventParam)) ) - self._connection.send(data) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return self._connection.send(data) def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True self._connection.close(code=code, reason=reason) def parse_event(self, data: str | bytes) -> RealtimeServerEvent: @@ -502,6 +858,161 @@ def parse_event(self, data: str | bytes) -> RealtimeServerEvent: RealtimeServerEvent, construct_type_unchecked(value=json.loads(data), type_=cast(Any, RealtimeServerEvent)) ) + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(1, self._max_retries + 1): + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data)) + except Exception: + log.warning("Failed to flush send queue after reconnect", exc_info=True) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("conversation.created", my_handler) + + Or as a decorator:: + + @connection.on("conversation.created") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> RealtimeConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, RealtimeErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + class RealtimeConnectionManager: """ @@ -532,6 +1043,11 @@ def __init__( extra_query: Query, extra_headers: Headers, websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> None: self.__client = client self.__call_id = call_id @@ -540,10 +1056,66 @@ def __init__( self.__extra_query = extra_query self.__extra_headers = extra_headers self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: RealtimeClientEvent | RealtimeClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(event) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``RealtimeConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> RealtimeConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[RealtimeConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator def __enter__(self) -> RealtimeConnection: """ - 👋 If your application doesn't work well with the context manager approach then you + If your application doesn't work well with the context manager approach then you can call this method directly to initiate a connection. **Warning**: You must remember to close the connection with `.close()`. @@ -554,15 +1126,35 @@ def __enter__(self) -> RealtimeConnection: connection.close() ``` """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = RealtimeConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: try: from websockets.sync.client import connect except ImportError as exc: raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc - extra_query = self.__extra_query self.__client._refresh_api_key() auth_headers = self.__client.auth_headers - extra_query = self.__extra_query if self.__call_id is not omit: extra_query = {**extra_query, "call_id": self.__call_id} if is_azure_client(self.__client): @@ -583,24 +1175,18 @@ def __enter__(self) -> RealtimeConnection: if self.__websocket_connection_options: log.debug("Connection options: %s", self.__websocket_connection_options) - self.__connection = RealtimeConnection( - connect( - str(url), - user_agent_header=self.__client.user_agent, - additional_headers=_merge_mappings( - { - **auth_headers, - }, - self.__extra_headers, - ), - **self.__websocket_connection_options, - ) + return connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **auth_headers, + }, + extra_headers, + ), + **self.__websocket_connection_options, ) - return self.__connection - - enter = __enter__ - def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 360c5afa1a..ab5f7688a5 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -3,10 +3,25 @@ from __future__ import annotations import json +import time +import random import logging from copy import copy from types import TracebackType -from typing import TYPE_CHECKING, Any, List, Type, Union, Iterable, Iterator, Optional, AsyncIterator, cast +from typing import ( + TYPE_CHECKING, + Any, + List, + Type, + Union, + Callable, + Iterable, + Iterator, + Optional, + Awaitable, + AsyncIterator, + cast, +) from functools import partial from typing_extensions import Literal, overload @@ -38,8 +53,10 @@ InputTokensWithStreamingResponse, AsyncInputTokensWithStreamingResponse, ) -from ..._exceptions import OpenAIError +from ..._exceptions import OpenAIError, WebSocketConnectionClosedError +from ..._send_queue import SendQueue from ..._base_client import _merge_mappings, make_request_options +from ..._event_handler import EventHandlerRegistry from ...types.responses import ( response_create_params, response_compact_params, @@ -54,6 +71,7 @@ from ...types.responses.response import Response from ...types.responses.tool_param import ToolParam, ParseableToolParam from ...types.shared_params.metadata import Metadata +from ...types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close from ...types.shared_params.reasoning import Reasoning from ...types.responses.parsed_response import ParsedResponse from ...lib.streaming.responses._responses import ResponseStreamManager, AsyncResponseStreamManager @@ -61,6 +79,7 @@ from ...types.websocket_connection_options import WebSocketConnectionOptions from ...types.responses.response_includable import ResponseIncludable from ...types.shared_params.responses_model import ResponsesModel +from ...types.responses.response_error_event import ResponseErrorEvent from ...types.responses.response_input_param import ResponseInputParam from ...types.responses.response_prompt_param import ResponsePromptParam from ...types.responses.response_stream_event import ResponseStreamEvent @@ -1735,6 +1754,11 @@ def connect( extra_query: Query = {}, extra_headers: Headers = {}, websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> ResponsesConnectionManager: """Connect to a persistent Responses API WebSocket. @@ -1745,6 +1769,11 @@ def connect( extra_query=extra_query, extra_headers=extra_headers, websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, ) @@ -3406,6 +3435,11 @@ def connect( extra_query: Query = {}, extra_headers: Headers = {}, websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> AsyncResponsesConnectionManager: """Connect to a persistent Responses API WebSocket. @@ -3416,6 +3450,11 @@ def connect( extra_query=extra_query, extra_headers=extra_headers, websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, ) @@ -3586,8 +3625,31 @@ class AsyncResponsesConnection: _connection: AsyncWebSocketConnection - def __init__(self, connection: AsyncWebSocketConnection) -> None: + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) self.response = AsyncResponsesResponseResource(self) @@ -3596,13 +3658,22 @@ async def __aiter__(self) -> AsyncIterator[ResponsesServerEvent]: An infinite-iterator that will continue to yield events until the connection is closed. """ - from websockets.exceptions import ConnectionClosedOK + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError - try: - while True: + while True: + try: yield await self.recv() - except ConnectionClosedOK: - return + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise async def recv(self) -> ResponsesServerEvent: """ @@ -3630,12 +3701,24 @@ async def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> if isinstance(event, BaseModel) else json.dumps(await async_maybe_transform(event, ResponsesClientEventParam)) ) - await self._connection.send(data) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + await self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return await self._connection.send(data) async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True await self._connection.close(code=code, reason=reason) def parse_event(self, data: str | bytes) -> ResponsesServerEvent: @@ -3649,6 +3732,173 @@ def parse_event(self, data: str | bytes) -> ResponsesServerEvent: construct_type_unchecked(value=json.loads(data), type_=cast(Any, ResponsesServerEvent)), ) + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(1, self._max_retries + 1): + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send) + except Exception: + log.warning("Failed to flush send queue after reconnect", exc_info=True) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.audio.delta", my_handler) + + Or as a decorator:: + + @connection.on("response.audio.delta") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ResponseErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + class AsyncResponsesConnectionManager: """ @@ -3677,16 +3927,77 @@ def __init__( extra_query: Query, extra_headers: Headers, websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> None: self.__client = client self.__connection: AsyncResponsesConnection | None = None self.__extra_query = extra_query self.__extra_headers = extra_headers self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(event) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator async def __aenter__(self) -> AsyncResponsesConnection: """ - 👋 If your application doesn't work well with the context manager approach then you + If your application doesn't work well with the context manager approach then you can call this method directly to initiate a connection. **Warning**: You must remember to close the connection with `.close()`. @@ -3697,6 +4008,28 @@ async def __aenter__(self) -> AsyncResponsesConnection: await connection.close() ``` """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: try: from websockets.asyncio.client import connect except ImportError as exc: @@ -3705,31 +4038,25 @@ async def __aenter__(self) -> AsyncResponsesConnection: url = self._prepare_url().copy_with( params={ **self.__client.base_url.params, - **self.__extra_query, + **extra_query, }, ) log.debug("Connecting to %s", url) if self.__websocket_connection_options: log.debug("Connection options: %s", self.__websocket_connection_options) - self.__connection = AsyncResponsesConnection( - await connect( - str(url), - user_agent_header=self.__client.user_agent, - additional_headers=_merge_mappings( - { - **self.__client.auth_headers, - }, - self.__extra_headers, - ), - **self.__websocket_connection_options, - ) + return await connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + }, + extra_headers, + ), + **self.__websocket_connection_options, ) - return self.__connection - - enter = __aenter__ - def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) @@ -3755,8 +4082,31 @@ class ResponsesConnection: _connection: WebSocketConnection - def __init__(self, connection: WebSocketConnection) -> None: + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) self.response = ResponsesResponseResource(self) @@ -3765,13 +4115,22 @@ def __iter__(self) -> Iterator[ResponsesServerEvent]: An infinite-iterator that will continue to yield events until the connection is closed. """ - from websockets.exceptions import ConnectionClosedOK + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError - try: - while True: + while True: + try: yield self.recv() - except ConnectionClosedOK: - return + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise def recv(self) -> ResponsesServerEvent: """ @@ -3799,12 +4158,24 @@ def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: if isinstance(event, BaseModel) else json.dumps(maybe_transform(event, ResponsesClientEventParam)) ) - self._connection.send(data) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return self._connection.send(data) def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True self._connection.close(code=code, reason=reason) def parse_event(self, data: str | bytes) -> ResponsesServerEvent: @@ -3818,6 +4189,161 @@ def parse_event(self, data: str | bytes) -> ResponsesServerEvent: construct_type_unchecked(value=json.loads(data), type_=cast(Any, ResponsesServerEvent)), ) + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(1, self._max_retries + 1): + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data)) + except Exception: + log.warning("Failed to flush send queue after reconnect", exc_info=True) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.audio.delta", my_handler) + + Or as a decorator:: + + @connection.on("response.audio.delta") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, ResponseErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + class ResponsesConnectionManager: """ @@ -3846,16 +4372,77 @@ def __init__( extra_query: Query, extra_headers: Headers, websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, ) -> None: self.__client = client self.__connection: ResponsesConnection | None = None self.__extra_query = extra_query self.__extra_headers = extra_headers self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: ResponsesClientEvent | ResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(event) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``ResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator def __enter__(self) -> ResponsesConnection: """ - 👋 If your application doesn't work well with the context manager approach then you + If your application doesn't work well with the context manager approach then you can call this method directly to initiate a connection. **Warning**: You must remember to close the connection with `.close()`. @@ -3866,6 +4453,28 @@ def __enter__(self) -> ResponsesConnection: connection.close() ``` """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = ResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: try: from websockets.sync.client import connect except ImportError as exc: @@ -3874,31 +4483,25 @@ def __enter__(self) -> ResponsesConnection: url = self._prepare_url().copy_with( params={ **self.__client.base_url.params, - **self.__extra_query, + **extra_query, }, ) log.debug("Connecting to %s", url) if self.__websocket_connection_options: log.debug("Connection options: %s", self.__websocket_connection_options) - self.__connection = ResponsesConnection( - connect( - str(url), - user_agent_header=self.__client.user_agent, - additional_headers=_merge_mappings( - { - **self.__client.auth_headers, - }, - self.__extra_headers, - ), - **self.__websocket_connection_options, - ) + return connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + }, + extra_headers, + ), + **self.__websocket_connection_options, ) - return self.__connection - - enter = __enter__ - def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: base_url = httpx.URL(self.__client.websocket_base_url) diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py index 6074eb0a1d..dc2c8a348c 100644 --- a/src/openai/types/__init__.py +++ b/src/openai/types/__init__.py @@ -85,6 +85,10 @@ from .file_chunking_strategy import FileChunkingStrategy as FileChunkingStrategy from .image_gen_stream_event import ImageGenStreamEvent as ImageGenStreamEvent from .upload_complete_params import UploadCompleteParams as UploadCompleteParams +from .websocket_reconnection import ( + ReconnectingEvent as ReconnectingEvent, + ReconnectingOverrides as ReconnectingOverrides, +) from .container_create_params import ContainerCreateParams as ContainerCreateParams from .container_list_response import ContainerListResponse as ContainerListResponse from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py index 3e5fb70c5f..f07ff7c049 100644 --- a/src/openai/types/responses/response_input_file.py +++ b/src/openai/types/responses/response_input_file.py @@ -14,6 +14,13 @@ class ResponseInputFile(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" + detail: Optional[Literal["low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `low` for the default rendering behavior, or `high` to render the file at + higher quality. Defaults to `low`. + """ + file_data: Optional[str] = None """The content of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_content.py b/src/openai/types/responses/response_input_file_content.py index f0dfef55d0..a0c2de4823 100644 --- a/src/openai/types/responses/response_input_file_content.py +++ b/src/openai/types/responses/response_input_file_content.py @@ -14,6 +14,13 @@ class ResponseInputFileContent(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" + detail: Optional[Literal["low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `low` for the default rendering behavior, or `high` to render the file at + higher quality. Defaults to `low`. + """ + file_data: Optional[str] = None """The base64-encoded data of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_content_param.py b/src/openai/types/responses/response_input_file_content_param.py index 376f6c7a45..4206ea09f3 100644 --- a/src/openai/types/responses/response_input_file_content_param.py +++ b/src/openai/types/responses/response_input_file_content_param.py @@ -14,6 +14,13 @@ class ResponseInputFileContentParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" + detail: Literal["low", "high"] + """The detail level of the file to be sent to the model. + + Use `low` for the default rendering behavior, or `high` to render the file at + higher quality. Defaults to `low`. + """ + file_data: Optional[str] """The base64-encoded data of the file to be sent to the model.""" diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py index 8b5da20245..a6bdb6e18c 100644 --- a/src/openai/types/responses/response_input_file_param.py +++ b/src/openai/types/responses/response_input_file_param.py @@ -14,6 +14,13 @@ class ResponseInputFileParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" + detail: Literal["low", "high"] + """The detail level of the file to be sent to the model. + + Use `low` for the default rendering behavior, or `high` to render the file at + higher quality. Defaults to `low`. + """ + file_data: str """The content of the file to be sent to the model.""" diff --git a/src/openai/types/websocket_reconnection.py b/src/openai/types/websocket_reconnection.py new file mode 100644 index 0000000000..9ba66d0ca5 --- /dev/null +++ b/src/openai/types/websocket_reconnection.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import dataclasses +from typing_extensions import TypedDict + +from .._types import Query, Headers + + +@dataclasses.dataclass(frozen=True) +class ReconnectingEvent: + """Information about a reconnection attempt, passed to the ``on_reconnecting`` handler.""" + + attempt: int + """Which retry attempt this is (1-based).""" + + max_attempts: int + """Total attempts that will be made.""" + + delay: float + """Delay in seconds before this attempt connects.""" + + close_code: int + """The WebSocket close code that triggered reconnection.""" + + extra_query: Query + """The current query parameters.""" + + extra_headers: Headers + """The current headers.""" + + +class ReconnectingOverrides(TypedDict, total=False): + """Optional overrides returned from the ``on_reconnecting`` handler + to customize the next reconnection attempt.""" + + extra_query: Query + """If provided, assigns the query parameters for the next connection.""" + + extra_headers: Headers + """If provided, assigns the headers for the next connection.""" + + abort: bool + """If set to ``True``, will stop attempting to reconnect.""" + + +# RFC 6455 §7.4.1 +_RECOVERABLE_CLOSE_CODES: frozenset[int] = frozenset( + { + 1001, # Going away (server shutting down) + 1005, # No status code (abnormal) + 1006, # Abnormal closure (network drop) + 1011, # Internal server error + 1012, # Service restart + 1013, # Try again later + 1015, # TLS handshake failure + } +) + + +def is_recoverable_close(code: int) -> bool: + """Return ``True`` if the WebSocket close *code* is worth retrying.""" + return code in _RECOVERABLE_CLOSE_CODES diff --git a/tests/api_resources/audio/test_speech.py b/tests/api_resources/audio/test_speech.py index a42c77126d..93ede5193d 100644 --- a/tests/api_resources/audio/test_speech.py +++ b/tests/api_resources/audio/test_speech.py @@ -27,8 +27,8 @@ def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None: respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = client.audio.speech.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} @@ -39,8 +39,8 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = client.audio.speech.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", instructions="instructions", response_format="mp3", speed=0.25, @@ -56,8 +56,8 @@ def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> No response = client.audio.speech.with_raw_response.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", ) assert response.is_closed is True @@ -71,8 +71,8 @@ def test_streaming_response_create(self, client: OpenAI, respx_mock: MockRouter) respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) with client.audio.speech.with_streaming_response.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -94,8 +94,8 @@ async def test_method_create(self, async_client: AsyncOpenAI, respx_mock: MockRo respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = await async_client.audio.speech.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", ) assert isinstance(speech, _legacy_response.HttpxBinaryResponseContent) assert speech.json() == {"foo": "bar"} @@ -106,8 +106,8 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) speech = await async_client.audio.speech.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", instructions="instructions", response_format="mp3", speed=0.25, @@ -123,8 +123,8 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI, respx_mock: response = await async_client.audio.speech.with_raw_response.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", ) assert response.is_closed is True @@ -138,8 +138,8 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI, respx_ respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"})) async with async_client.audio.speech.with_streaming_response.create( input="string", - model="string", - voice="string", + model="tts-1", + voice="alloy", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/beta/test_assistants.py b/tests/api_resources/beta/test_assistants.py index 3e85b56dcc..91ff13dded 100644 --- a/tests/api_resources/beta/test_assistants.py +++ b/tests/api_resources/beta/test_assistants.py @@ -149,7 +149,7 @@ def test_method_update_with_all_params(self, client: OpenAI) -> None: description="description", instructions="instructions", metadata={"foo": "string"}, - model="string", + model="gpt-5", name="name", reasoning_effort="none", response_format="auto", @@ -414,7 +414,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> description="description", instructions="instructions", metadata={"foo": "string"}, - model="string", + model="gpt-5", name="name", reasoning_effort="none", response_format="auto", diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index f392c86729..7163511951 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -248,7 +248,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, response_format="auto", stream=False, @@ -343,7 +343,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, response_format="auto", temperature=1, @@ -649,7 +649,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, response_format="auto", stream=False, @@ -744,7 +744,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, response_format="auto", temperature=1, diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index 3a6b36864d..aa85871325 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -57,7 +57,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", @@ -148,7 +148,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", @@ -607,7 +607,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", @@ -698,7 +698,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="string", + model="gpt-5.4", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index c55c132697..a75764b5e9 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -48,7 +48,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: model="gpt-5.4", audio={ "format": "wav", - "voice": "string", + "voice": "alloy", }, frequency_penalty=-2, function_call="none", @@ -182,7 +182,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: stream=True, audio={ "format": "wav", - "voice": "string", + "voice": "alloy", }, frequency_penalty=-2, function_call="none", @@ -491,7 +491,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn model="gpt-5.4", audio={ "format": "wav", - "voice": "string", + "voice": "alloy", }, frequency_penalty=-2, function_call="none", @@ -625,7 +625,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn stream=True, audio={ "format": "wav", - "voice": "string", + "voice": "alloy", }, frequency_penalty=-2, function_call="none", diff --git a/tests/api_resources/realtime/test_calls.py b/tests/api_resources/realtime/test_calls.py index 9e2810841d..43ab9afe01 100644 --- a/tests/api_resources/realtime/test_calls.py +++ b/tests/api_resources/realtime/test_calls.py @@ -48,7 +48,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "string", + "model": "whisper-1", "prompt": "prompt", }, "turn_detection": { @@ -67,13 +67,13 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou "type": "audio/pcm", }, "speed": 0.25, - "voice": "string", + "voice": "alloy", }, }, "include": ["item.input_audio_transcription.logprobs"], "instructions": "instructions", - "max_output_tokens": 0, - "model": "string", + "max_output_tokens": "inf", + "model": "gpt-realtime", "output_modalities": ["text"], "prompt": { "id": "id", @@ -147,7 +147,7 @@ def test_method_accept_with_all_params(self, client: OpenAI) -> None: "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "string", + "model": "whisper-1", "prompt": "prompt", }, "turn_detection": { @@ -166,13 +166,13 @@ def test_method_accept_with_all_params(self, client: OpenAI) -> None: "type": "audio/pcm", }, "speed": 0.25, - "voice": "string", + "voice": "alloy", }, }, include=["item.input_audio_transcription.logprobs"], instructions="instructions", - max_output_tokens=0, - model="string", + max_output_tokens="inf", + model="gpt-realtime", output_modalities=["text"], prompt={ "id": "id", @@ -386,7 +386,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "string", + "model": "whisper-1", "prompt": "prompt", }, "turn_detection": { @@ -405,13 +405,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re "type": "audio/pcm", }, "speed": 0.25, - "voice": "string", + "voice": "alloy", }, }, "include": ["item.input_audio_transcription.logprobs"], "instructions": "instructions", - "max_output_tokens": 0, - "model": "string", + "max_output_tokens": "inf", + "model": "gpt-realtime", "output_modalities": ["text"], "prompt": { "id": "id", @@ -485,7 +485,7 @@ async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "string", + "model": "whisper-1", "prompt": "prompt", }, "turn_detection": { @@ -504,13 +504,13 @@ async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> "type": "audio/pcm", }, "speed": 0.25, - "voice": "string", + "voice": "alloy", }, }, include=["item.input_audio_transcription.logprobs"], instructions="instructions", - max_output_tokens=0, - model="string", + max_output_tokens="inf", + model="gpt-realtime", output_modalities=["text"], prompt={ "id": "id", diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py index bfa0deac55..a354019eac 100644 --- a/tests/api_resources/realtime/test_client_secrets.py +++ b/tests/api_resources/realtime/test_client_secrets.py @@ -40,7 +40,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "string", + "model": "whisper-1", "prompt": "prompt", }, "turn_detection": { @@ -59,13 +59,13 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "type": "audio/pcm", }, "speed": 0.25, - "voice": "string", + "voice": "alloy", }, }, "include": ["item.input_audio_transcription.logprobs"], "instructions": "instructions", - "max_output_tokens": 0, - "model": "string", + "max_output_tokens": "inf", + "model": "gpt-realtime", "output_modalities": ["text"], "prompt": { "id": "id", @@ -136,7 +136,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "noise_reduction": {"type": "near_field"}, "transcription": { "language": "language", - "model": "string", + "model": "whisper-1", "prompt": "prompt", }, "turn_detection": { @@ -155,13 +155,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "type": "audio/pcm", }, "speed": 0.25, - "voice": "string", + "voice": "alloy", }, }, "include": ["item.input_audio_transcription.logprobs"], "instructions": "instructions", - "max_output_tokens": 0, - "model": "string", + "max_output_tokens": "inf", + "model": "gpt-realtime", "output_modalities": ["text"], "prompt": { "id": "id", diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py index a8fb0e59eb..d0081aaca2 100644 --- a/tests/api_resources/test_completions.py +++ b/tests/api_resources/test_completions.py @@ -20,7 +20,7 @@ class TestCompletions: @parametrize def test_method_create_overload_1(self, client: OpenAI) -> None: completion = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) assert_matches_type(Completion, completion, path=["response"]) @@ -28,7 +28,7 @@ def test_method_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: completion = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", best_of=0, echo=True, @@ -55,7 +55,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_1(self, client: OpenAI) -> None: response = client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) @@ -67,7 +67,7 @@ def test_raw_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: with client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) as response: assert not response.is_closed @@ -81,7 +81,7 @@ def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: @parametrize def test_method_create_overload_2(self, client: OpenAI) -> None: completion_stream = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -90,7 +90,7 @@ def test_method_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: completion_stream = client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, best_of=0, @@ -117,7 +117,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: @parametrize def test_raw_response_create_overload_2(self, client: OpenAI) -> None: response = client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -129,7 +129,7 @@ def test_raw_response_create_overload_2(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: with client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) as response: @@ -150,7 +150,7 @@ class TestAsyncCompletions: @parametrize async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: completion = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) assert_matches_type(Completion, completion, path=["response"]) @@ -158,7 +158,7 @@ async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: completion = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", best_of=0, echo=True, @@ -185,7 +185,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: response = await async_client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) @@ -197,7 +197,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: async with async_client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", ) as response: assert not response.is_closed @@ -211,7 +211,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncOpe @parametrize async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: completion_stream = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -220,7 +220,7 @@ async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: completion_stream = await async_client.completions.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, best_of=0, @@ -247,7 +247,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: response = await async_client.completions.with_raw_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) @@ -259,7 +259,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) - @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: async with async_client.completions.with_streaming_response.create( - model="string", + model="gpt-3.5-turbo-instruct", prompt="This is a test.", stream=True, ) as response: diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 9862b79c65..13cbc0acb7 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -28,7 +28,7 @@ def test_method_create_variation(self, client: OpenAI) -> None: def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: image = client.images.create_variation( image=b"Example data", - model="string", + model="gpt-image-1.5", n=1, response_format="url", size="1024x1024", @@ -76,7 +76,7 @@ def test_method_edit_with_all_params_overload_1(self, client: OpenAI) -> None: background="transparent", input_fidelity="high", mask=b"Example data", - model="string", + model="gpt-image-1.5", n=1, output_compression=100, output_format="png", @@ -133,7 +133,7 @@ def test_method_edit_with_all_params_overload_2(self, client: OpenAI) -> None: background="transparent", input_fidelity="high", mask=b"Example data", - model="string", + model="gpt-image-1.5", n=1, output_compression=100, output_format="png", @@ -184,7 +184,7 @@ def test_method_generate_with_all_params_overload_1(self, client: OpenAI) -> Non image = client.images.generate( prompt="A cute baby sea otter", background="transparent", - model="string", + model="gpt-image-1.5", moderation="low", n=1, output_compression=100, @@ -237,7 +237,7 @@ def test_method_generate_with_all_params_overload_2(self, client: OpenAI) -> Non prompt="A cute baby sea otter", stream=True, background="transparent", - model="string", + model="gpt-image-1.5", moderation="low", n=1, output_compression=100, @@ -293,7 +293,7 @@ async def test_method_create_variation(self, async_client: AsyncOpenAI) -> None: async def test_method_create_variation_with_all_params(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.create_variation( image=b"Example data", - model="string", + model="gpt-image-1.5", n=1, response_format="url", size="1024x1024", @@ -341,7 +341,7 @@ async def test_method_edit_with_all_params_overload_1(self, async_client: AsyncO background="transparent", input_fidelity="high", mask=b"Example data", - model="string", + model="gpt-image-1.5", n=1, output_compression=100, output_format="png", @@ -398,7 +398,7 @@ async def test_method_edit_with_all_params_overload_2(self, async_client: AsyncO background="transparent", input_fidelity="high", mask=b"Example data", - model="string", + model="gpt-image-1.5", n=1, output_compression=100, output_format="png", @@ -449,7 +449,7 @@ async def test_method_generate_with_all_params_overload_1(self, async_client: As image = await async_client.images.generate( prompt="A cute baby sea otter", background="transparent", - model="string", + model="gpt-image-1.5", moderation="low", n=1, output_compression=100, @@ -502,7 +502,7 @@ async def test_method_generate_with_all_params_overload_2(self, async_client: As prompt="A cute baby sea otter", stream=True, background="transparent", - model="string", + model="gpt-image-1.5", moderation="low", n=1, output_compression=100, diff --git a/tests/api_resources/test_moderations.py b/tests/api_resources/test_moderations.py index 870c9e342f..8487d4a7a6 100644 --- a/tests/api_resources/test_moderations.py +++ b/tests/api_resources/test_moderations.py @@ -28,7 +28,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: moderation = client.moderations.create( input="I want to kill them.", - model="string", + model="omni-moderation-latest", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) @@ -73,7 +73,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: moderation = await async_client.moderations.create( input="I want to kill them.", - model="string", + model="omni-moderation-latest", ) assert_matches_type(ModerationCreateResponse, moderation, path=["response"]) diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index deaf35970f..0b871d525d 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -40,7 +40,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: include=["file_search_call.results"], input="string", instructions="instructions", - max_output_tokens=0, + max_output_tokens=16, max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", @@ -128,7 +128,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: include=["file_search_call.results"], input="string", instructions="instructions", - max_output_tokens=0, + max_output_tokens=16, max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", @@ -451,7 +451,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn include=["file_search_call.results"], input="string", instructions="instructions", - max_output_tokens=0, + max_output_tokens=16, max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", @@ -539,7 +539,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn include=["file_search_call.results"], input="string", instructions="instructions", - max_output_tokens=0, + max_output_tokens=16, max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", diff --git a/tests/api_resources/test_videos.py b/tests/api_resources/test_videos.py index 73acf6d05d..d6b51f9674 100644 --- a/tests/api_resources/test_videos.py +++ b/tests/api_resources/test_videos.py @@ -41,7 +41,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: video = client.videos.create( prompt="x", input_reference=b"Example data", - model="string", + model="sora-2", seconds="4", size="720x1280", ) @@ -442,7 +442,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> video = await async_client.videos.create( prompt="x", input_reference=b"Example data", - model="string", + model="sora-2", seconds="4", size="720x1280", ) diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index 0f6fb04d7d..dcf85bd7c7 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -35,6 +35,15 @@ def test_multiple_files() -> None: assert query == {"documents": [{}, {}]} +def test_top_level_file_array() -> None: + query = {"files": [b"file one", b"file two"], "title": "hello"} + assert extract_files(query, paths=[["files", ""]]) == [ + ("files[]", b"file one"), + ("files[]", b"file two"), + ] + assert query == {"title": "hello"} + + @pytest.mark.parametrize( "query,paths,expected", [ diff --git a/tests/test_send_queue.py b/tests/test_send_queue.py new file mode 100644 index 0000000000..61db916bc4 --- /dev/null +++ b/tests/test_send_queue.py @@ -0,0 +1,128 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import pytest + +from openai._exceptions import WebSocketQueueFullError +from openai._send_queue import SendQueue + + +class TestSendQueue: + def test_enqueue_and_drain(self) -> None: + q = SendQueue() + q.enqueue('{"type": "session.update"}') + q.enqueue('{"type": "response.create"}') + assert len(q) == 2 + + items = q.drain() + assert items == ['{"type": "session.update"}', '{"type": "response.create"}'] + assert len(q) == 0 + + def test_enqueue_respects_byte_limit(self) -> None: + q = SendQueue(max_bytes=10) + q.enqueue("12345") # 5 bytes, fits + with pytest.raises(WebSocketQueueFullError): + q.enqueue("123456") # 6 bytes, would exceed 10 + assert len(q) == 1 + + def test_drain_empties_queue(self) -> None: + q = SendQueue() + q.enqueue("hello") + q.drain() + assert len(q) == 0 + assert not q + + def test_bool(self) -> None: + q = SendQueue() + assert not q + q.enqueue("x") + assert q + + def test_flush_sync(self) -> None: + q = SendQueue() + q.enqueue("a") + q.enqueue("b") + q.enqueue("c") + + sent: list[str] = [] + q.flush_sync(sent.append) + assert sent == ["a", "b", "c"] + assert len(q) == 0 + + def test_flush_sync_requeues_on_failure(self) -> None: + q = SendQueue() + q.enqueue("a") + q.enqueue("b") + q.enqueue("c") + + sent: list[str] = [] + + def failing_send(data: str) -> None: + if data == "b": + raise RuntimeError("send failed") + sent.append(data) + + with pytest.raises(RuntimeError, match="send failed"): + q.flush_sync(failing_send) + + assert sent == ["a"] + # b and c should be re-queued + remaining = q.drain() + assert remaining == ["b", "c"] + + @pytest.mark.asyncio + async def test_flush_async(self) -> None: + q = SendQueue() + q.enqueue("a") + q.enqueue("b") + + sent: list[str] = [] + + async def async_send(data: str) -> None: + sent.append(data) + + await q.flush_async(async_send) + assert sent == ["a", "b"] + assert len(q) == 0 + + @pytest.mark.asyncio + async def test_flush_async_requeues_on_failure(self) -> None: + q = SendQueue() + q.enqueue("a") + q.enqueue("b") + q.enqueue("c") + + sent: list[str] = [] + + async def failing_send(data: str) -> None: + if data == "b": + raise RuntimeError("send failed") + sent.append(data) + + with pytest.raises(RuntimeError, match="send failed"): + await q.flush_async(failing_send) + + assert sent == ["a"] + remaining = q.drain() + assert remaining == ["b", "c"] + + def test_flush_sync_preserves_new_items_on_failure(self) -> None: + """If items are enqueued after flush starts and flush fails, + the re-queued items should come before the new items.""" + q = SendQueue() + q.enqueue("a") + q.enqueue("b") + + def failing_send(data: str) -> None: + if data == "b": + # Simulate another thread enqueuing during flush + q.enqueue("new") + raise RuntimeError("fail") + + with pytest.raises(RuntimeError): + q.flush_sync(failing_send) + + # "b" (failed) should come before "new" (added during flush) + remaining = q.drain() + assert remaining == ["b", "new"] From f9d2d1359688a6247ecba858fc687173c480c9c8 Mon Sep 17 00:00:00 2001 From: Cameron McAteer <246350779+cameron-mcateer@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:52:29 -0400 Subject: [PATCH 295/408] fix(api): correct prompt_cache_retention enum value from in-memory to in_memory (#1822) Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> --- .../resources/chat/completions/completions.py | 24 +++++++------- src/openai/resources/responses/responses.py | 32 +++++++++---------- .../types/chat/completion_create_params.py | 2 +- src/openai/types/responses/response.py | 2 +- .../types/responses/response_create_params.py | 2 +- .../types/responses/responses_client_event.py | 2 +- .../responses/responses_client_event_param.py | 2 +- tests/api_resources/chat/test_completions.py | 8 ++--- tests/api_resources/test_responses.py | 8 ++--- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 845bd1a1e1..8b4fc12ae9 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -109,7 +109,7 @@ def parse( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, @@ -264,7 +264,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -571,7 +571,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -877,7 +877,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -1182,7 +1182,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -1461,7 +1461,7 @@ def stream( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, @@ -1612,7 +1612,7 @@ async def parse( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, @@ -1767,7 +1767,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -2074,7 +2074,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -2380,7 +2380,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -2685,7 +2685,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, safety_identifier: str | Omit = omit, @@ -2964,7 +2964,7 @@ def stream( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, seed: Optional[int] | Omit = omit, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index ab5f7688a5..cb9a09bf22 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -146,7 +146,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -396,7 +396,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -645,7 +645,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -892,7 +892,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -995,7 +995,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -1036,7 +1036,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -1187,7 +1187,7 @@ def parse( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -1823,7 +1823,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2073,7 +2073,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2322,7 +2322,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2569,7 +2569,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2672,7 +2672,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2713,7 +2713,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -2868,7 +2868,7 @@ async def parse( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -4543,7 +4543,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, @@ -4623,7 +4623,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 8e71ccbe41..0379ee0865 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -185,7 +185,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] """The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index ada0783bce..0d2491ea7c 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -214,7 +214,7 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] = None + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None """The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index bf7170da1f..a04495f40a 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -152,7 +152,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] """The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py index 2bc6f899c5..5f9e73c61f 100644 --- a/src/openai/types/responses/responses_client_event.py +++ b/src/openai/types/responses/responses_client_event.py @@ -184,7 +184,7 @@ class ResponsesClientEvent(BaseModel): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] = None + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None """The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py index 08596ef9ea..249c812116 100644 --- a/src/openai/types/responses/responses_client_event_param.py +++ b/src/openai/types/responses/responses_client_event_param.py @@ -185,7 +185,7 @@ class ResponsesClientEventParam(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ - prompt_cache_retention: Optional[Literal["in-memory", "24h"]] + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] """The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index a75764b5e9..ea3066a505 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -73,7 +73,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", @@ -207,7 +207,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", @@ -516,7 +516,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", @@ -650,7 +650,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, safety_identifier="safety-identifier-1234", diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 0b871d525d..096b983be2 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -52,7 +52,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "version": "version", }, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning={ "effort": "none", "generate_summary": "auto", @@ -140,7 +140,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "version": "version", }, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning={ "effort": "none", "generate_summary": "auto", @@ -463,7 +463,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "version": "version", }, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning={ "effort": "none", "generate_summary": "auto", @@ -551,7 +551,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "version": "version", }, prompt_cache_key="prompt-cache-key-1234", - prompt_cache_retention="in-memory", + prompt_cache_retention="in_memory", reasoning={ "effort": "none", "generate_summary": "auto", From 00b20910e3539842f21d86ab5928fb5216d3a765 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:04:42 +0000 Subject: [PATCH 296/408] chore(ci): remove release-doctor workflow --- .github/workflows/release-doctor.yml | 23 ----------------------- bin/check-release-environment | 25 ------------------------- 2 files changed, 48 deletions(-) delete mode 100644 .github/workflows/release-doctor.yml delete mode 100644 bin/check-release-environment diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml deleted file mode 100644 index 503de9d99a..0000000000 --- a/.github/workflows/release-doctor.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Release Doctor -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - release_doctor: - name: release doctor - runs-on: ubuntu-latest - environment: publish - if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') - - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Check release environment - run: | - bash ./bin/check-release-environment - env: - STAINLESS_API_KEY: ${{ secrets.STAINLESS_API_KEY }} - PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/bin/check-release-environment b/bin/check-release-environment deleted file mode 100644 index 044ed525d1..0000000000 --- a/bin/check-release-environment +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -errors=() - -if [ -z "${STAINLESS_API_KEY}" ]; then - errors+=("The STAINLESS_API_KEY secret has not been set. Please contact Stainless for an API key & set it in your organization secrets on GitHub.") -fi - -if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") -fi - -lenErrors=${#errors[@]} - -if [[ lenErrors -gt 0 ]]; then - echo -e "Found the following errors in the release environment:\n" - - for error in "${errors[@]}"; do - echo -e "- $error\n" - done - - exit 1 -fi - -echo "The environment is ready to push releases!" From 18f834a54f92ea827452471a46a4f442f251e2c8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 02:44:29 +0000 Subject: [PATCH 297/408] feat(api): api update --- .stats.yml | 4 ++-- src/openai/resources/responses/responses.py | 8 ++++++++ src/openai/types/responses/response_compact_params.py | 3 +++ tests/api_resources/test_responses.py | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f069d6c8b9..60efa98d0f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-7c540cce6eb30401259f4831ea9803b6d88501605d13734f98212cbb3b199e10.yml -openapi_spec_hash: 06e656be22bbb92689954253668b42fc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c6ba619ccbf87e56b4f464230d04401fd78ad924d2606176309d19ca281af5.yml +openapi_spec_hash: 5e4f2073040a12c26ce58e86a72fe47e config_hash: 1a88b104658b6c854117996c080ebe6b diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index cb9a09bf22..48705098cc 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1686,6 +1686,7 @@ def compact( instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1723,6 +1724,8 @@ def compact( prompt_cache_key: A key to use when reading from or writing to the prompt cache. + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1740,6 +1743,7 @@ def compact( "instructions": instructions, "previous_response_id": previous_response_id, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, }, response_compact_params.ResponseCompactParams, ), @@ -3367,6 +3371,7 @@ async def compact( instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3404,6 +3409,8 @@ async def compact( prompt_cache_key: A key to use when reading from or writing to the prompt cache. + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -3421,6 +3428,7 @@ async def compact( "instructions": instructions, "previous_response_id": previous_response_id, "prompt_cache_key": prompt_cache_key, + "prompt_cache_retention": prompt_cache_retention, }, response_compact_params.ResponseCompactParams, ), diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index 0b163a9e78..2575438b34 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -140,3 +140,6 @@ class ResponseCompactParams(TypedDict, total=False): prompt_cache_key: Optional[str] """A key to use when reading from or writing to the prompt cache.""" + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """How long to retain a prompt cache entry created by this request.""" diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 096b983be2..40405f61b2 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -388,6 +388,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None: instructions="instructions", previous_response_id="resp_123", prompt_cache_key="prompt_cache_key", + prompt_cache_retention="in_memory", ) assert_matches_type(CompactedResponse, response, path=["response"]) @@ -799,6 +800,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) - instructions="instructions", previous_response_id="resp_123", prompt_cache_key="prompt_cache_key", + prompt_cache_retention="in_memory", ) assert_matches_type(CompactedResponse, response, path=["response"]) From cb63de72923d89de4ba870e329038f99e10eb9dd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 19:50:54 +0000 Subject: [PATCH 298/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 60efa98d0f..9a106e0f6d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c6ba619ccbf87e56b4f464230d04401fd78ad924d2606176309d19ca281af5.yml openapi_spec_hash: 5e4f2073040a12c26ce58e86a72fe47e -config_hash: 1a88b104658b6c854117996c080ebe6b +config_hash: 50c98d8869a8cfdee2ab7dc664c4b6fe From c5b099cf9f08e2b0cda02a68f8cbfadbc35da4de Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 06:02:46 +0000 Subject: [PATCH 299/408] release: 2.33.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 527d2e30b0..7ef8288ed5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.32.0" + ".": "2.33.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2640bc3d63..effb1cc263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 2.33.0 (2026-04-28) + +Full Changelog: [v2.32.0...v2.33.0](https://github.com/openai/openai-python/compare/v2.32.0...v2.33.0) + +### Features + +* **api:** api update ([18f834a](https://github.com/openai/openai-python/commit/18f834a54f92ea827452471a46a4f442f251e2c8)) + + +### Bug Fixes + +* **api:** correct prompt_cache_retention enum value from in-memory to in_memory ([#1822](https://github.com/openai/openai-python/issues/1822)) ([f9d2d13](https://github.com/openai/openai-python/commit/f9d2d1359688a6247ecba858fc687173c480c9c8)) + + +### Chores + +* **ci:** remove release-doctor workflow ([00b2091](https://github.com/openai/openai-python/commit/00b20910e3539842f21d86ab5928fb5216d3a765)) + ## 2.32.0 (2026-04-15) Full Changelog: [v2.31.0...v2.32.0](https://github.com/openai/openai-python/compare/v2.31.0...v2.32.0) diff --git a/pyproject.toml b/pyproject.toml index d0d533e8a6..b2f4dd11cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.32.0" +version = "2.33.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a98695ba0e..b73f7aa7bd 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.32.0" # x-release-please-version +__version__ = "2.33.0" # x-release-please-version From 7912497291fb075fe6c688dadfed4825d7bd89d2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:51:31 +0000 Subject: [PATCH 300/408] perf(client): optimize file structure copying in multipart requests --- src/openai/_files.py | 56 ++++++++++- src/openai/_utils/__init__.py | 1 - src/openai/_utils/_utils.py | 15 --- src/openai/resources/audio/transcriptions.py | 13 ++- src/openai/resources/audio/translations.py | 13 ++- .../resources/containers/files/files.py | 13 ++- src/openai/resources/files.py | 13 ++- src/openai/resources/images.py | 23 +++-- src/openai/resources/skills/skills.py | 7 +- .../resources/skills/versions/versions.py | 13 ++- src/openai/resources/uploads/parts.py | 7 +- src/openai/resources/videos.py | 43 ++++---- tests/test_deepcopy.py | 58 ----------- tests/test_files.py | 99 ++++++++++++++++++- 14 files changed, 239 insertions(+), 135 deletions(-) delete mode 100644 tests/test_deepcopy.py diff --git a/src/openai/_files.py b/src/openai/_files.py index 7b23ca084a..4cc4f35d8f 100644 --- a/src/openai/_files.py +++ b/src/openai/_files.py @@ -3,8 +3,8 @@ import io import os import pathlib -from typing import overload -from typing_extensions import TypeGuard +from typing import Sequence, cast, overload +from typing_extensions import TypeVar, TypeGuard import anyio @@ -17,7 +17,9 @@ HttpxFileContent, HttpxRequestFiles, ) -from ._utils import is_tuple_t, is_mapping_t, is_sequence_t +from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t + +_T = TypeVar("_T") def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: @@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent: return await anyio.Path(file).read_bytes() return file + + +def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T: + """Copy only the containers along the given paths. + + Used to guard against mutation by extract_files without copying the entire structure. + Only dicts and lists that lie on a path are copied; everything else + is returned by reference. + + For example, given paths=[["foo", "files", "file"]] and the structure: + { + "foo": { + "bar": {"baz": {}}, + "files": {"file": } + } + } + The root dict, "foo", and "files" are copied (they lie on the path). + "bar" and "baz" are returned by reference (off the path). + """ + return _deepcopy_with_paths(item, paths, 0) + + +def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T: + if not paths: + return item + if is_mapping(item): + key_to_paths: dict[str, list[Sequence[str]]] = {} + for path in paths: + if index < len(path): + key_to_paths.setdefault(path[index], []).append(path) + + # if no path continues through this mapping, it won't be mutated and copying it is redundant + if not key_to_paths: + return item + + result = dict(item) + for key, subpaths in key_to_paths.items(): + if key in result: + result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1) + return cast(_T, result) + if is_list(item): + array_paths = [path for path in paths if index < len(path) and path[index] == ""] + + # if no path expects a list here, nothing will be mutated inside it - return by reference + if not array_paths: + return cast(_T, item) + return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item]) + return item diff --git a/src/openai/_utils/__init__.py b/src/openai/_utils/__init__.py index 52853aaf03..bbd79691fa 100644 --- a/src/openai/_utils/__init__.py +++ b/src/openai/_utils/__init__.py @@ -26,7 +26,6 @@ file_from_path as file_from_path, is_azure_client as is_azure_client, strip_not_given as strip_not_given, - deepcopy_minimal as deepcopy_minimal, get_async_library as get_async_library, maybe_coerce_float as maybe_coerce_float, get_required_header as get_required_header, diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index b1e8e0d041..e18fb09bee 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -181,21 +181,6 @@ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: return isinstance(obj, Iterable) -def deepcopy_minimal(item: _T) -> _T: - """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: - - - mappings, e.g. `dict` - - list - - This is done for performance reasons. - """ - if is_mapping(item): - return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) - if is_list(item): - return cast(_T, [deepcopy_minimal(entry) for entry in item]) - return item - - # copied from https://github.com/Rapptz/RoboDanny def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: size = len(seq) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index 25e6e0cb5e..e5f415f278 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -9,6 +9,7 @@ import httpx from ... import _legacy_response +from ..._files import deepcopy_with_paths from ..._types import ( Body, Omit, @@ -20,7 +21,7 @@ omit, not_given, ) -from ..._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import extract_files, required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -459,7 +460,7 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> str | Transcription | TranscriptionDiarized | TranscriptionVerbose | Stream[TranscriptionStreamEvent]: - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "model": model, @@ -473,7 +474,8 @@ def create( "stream": stream, "temperature": temperature, "timestamp_granularities": timestamp_granularities, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be @@ -913,7 +915,7 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Transcription | TranscriptionVerbose | TranscriptionDiarized | str | AsyncStream[TranscriptionStreamEvent]: - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "model": model, @@ -927,7 +929,8 @@ async def create( "stream": stream, "temperature": temperature, "timestamp_granularities": timestamp_granularities, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index 0751a65586..d6df25c1b7 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -9,8 +9,9 @@ import httpx from ... import _legacy_response +from ..._files import deepcopy_with_paths from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import extract_files, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -146,14 +147,15 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "model": model, "prompt": prompt, "response_format": response_format, "temperature": temperature, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be @@ -291,14 +293,15 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "model": model, "prompt": prompt, "response_format": response_format, "temperature": temperature, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py index f48adf3a2a..80e0d61e0a 100644 --- a/src/openai/resources/containers/files/files.py +++ b/src/openai/resources/containers/files/files.py @@ -16,8 +16,9 @@ ContentWithStreamingResponse, AsyncContentWithStreamingResponse, ) +from ...._files import deepcopy_with_paths from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, FileTypes, omit, not_given -from ...._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform +from ...._utils import extract_files, path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -89,11 +90,12 @@ def create( """ if not container_id: raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "file_id": file_id, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) if files: @@ -303,11 +305,12 @@ async def create( """ if not container_id: raise ValueError(f"Expected a non-empty value for `container_id` but received {container_id!r}") - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "file_id": file_id, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) if files: diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index b03f11b06a..0cc6b046ff 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -11,8 +11,9 @@ from .. import _legacy_response from ..types import FilePurpose, file_list_params, file_create_params +from .._files import deepcopy_with_paths from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from .._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._utils import extract_files, path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -116,12 +117,13 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "purpose": purpose, "expires_after": expires_after, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be @@ -441,12 +443,13 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "file": file, "purpose": purpose, "expires_after": expires_after, - } + }, + [["file"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 6959c2aeff..2555b6c5db 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -9,8 +9,9 @@ from .. import _legacy_response from ..types import image_edit_params, image_generate_params, image_create_variation_params +from .._files import deepcopy_with_paths from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given -from .._utils import extract_files, required_args, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._utils import extract_files, required_args, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -94,7 +95,7 @@ def create_variation( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "image": image, "model": model, @@ -102,7 +103,8 @@ def create_variation( "response_format": response_format, "size": size, "user": user, - } + }, + [["image"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) # It should be noted that the actual Content-Type header that will be @@ -484,7 +486,7 @@ def edit( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | Stream[ImageEditStreamEvent]: - body = deepcopy_minimal( + body = deepcopy_with_paths( { "image": image, "prompt": prompt, @@ -501,7 +503,8 @@ def edit( "size": size, "stream": stream, "user": user, - } + }, + [["image"], ["image", ""], ["mask"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) # It should be noted that the actual Content-Type header that will be @@ -986,7 +989,7 @@ async def create_variation( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "image": image, "model": model, @@ -994,7 +997,8 @@ async def create_variation( "response_format": response_format, "size": size, "user": user, - } + }, + [["image"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"]]) # It should be noted that the actual Content-Type header that will be @@ -1376,7 +1380,7 @@ async def edit( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ImagesResponse | AsyncStream[ImageEditStreamEvent]: - body = deepcopy_minimal( + body = deepcopy_with_paths( { "image": image, "prompt": prompt, @@ -1393,7 +1397,8 @@ async def edit( "size": size, "stream": stream, "user": user, - } + }, + [["image"], ["image", ""], ["mask"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["image", ""], ["mask"]]) # It should be noted that the actual Content-Type header that will be diff --git a/src/openai/resources/skills/skills.py b/src/openai/resources/skills/skills.py index f44fb24607..764ed65e84 100644 --- a/src/openai/resources/skills/skills.py +++ b/src/openai/resources/skills/skills.py @@ -17,6 +17,7 @@ ContentWithStreamingResponse, AsyncContentWithStreamingResponse, ) +from ..._files import deepcopy_with_paths from ..._types import ( Body, Omit, @@ -28,7 +29,7 @@ omit, not_given, ) -from ..._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import extract_files, path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -101,7 +102,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal({"files": files}) + body = deepcopy_with_paths({"files": files}, [["files", ""], ["files"]]) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) if extracted_files: # It should be noted that the actual Content-Type header that will be @@ -327,7 +328,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal({"files": files}) + body = deepcopy_with_paths({"files": files}, [["files", ""], ["files"]]) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) if extracted_files: # It should be noted that the actual Content-Type header that will be diff --git a/src/openai/resources/skills/versions/versions.py b/src/openai/resources/skills/versions/versions.py index 8b48075cc3..2259306a86 100644 --- a/src/openai/resources/skills/versions/versions.py +++ b/src/openai/resources/skills/versions/versions.py @@ -16,6 +16,7 @@ ContentWithStreamingResponse, AsyncContentWithStreamingResponse, ) +from ...._files import deepcopy_with_paths from ...._types import ( Body, Omit, @@ -27,7 +28,7 @@ omit, not_given, ) -from ...._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform +from ...._utils import extract_files, path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -95,11 +96,12 @@ def create( """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - body = deepcopy_minimal( + body = deepcopy_with_paths( { "default": default, "files": files, - } + }, + [["files", ""], ["files"]], ) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) if extracted_files: @@ -303,11 +305,12 @@ async def create( """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - body = deepcopy_minimal( + body = deepcopy_with_paths( { "default": default, "files": files, - } + }, + [["files", ""], ["files"]], ) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""], ["files"]]) if extracted_files: diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index cf09eea75e..3891fc028c 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -7,8 +7,9 @@ import httpx from ... import _legacy_response +from ..._files import deepcopy_with_paths from ..._types import Body, Query, Headers, NotGiven, FileTypes, not_given -from ..._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform +from ..._utils import extract_files, path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -79,7 +80,7 @@ def create( """ if not upload_id: raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") - body = deepcopy_minimal({"data": data}) + body = deepcopy_with_paths({"data": data}, [["data"]]) files = extract_files(cast(Mapping[str, object], body), paths=[["data"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. @@ -156,7 +157,7 @@ async def create( """ if not upload_id: raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}") - body = deepcopy_minimal({"data": data}) + body = deepcopy_with_paths({"data": data}, [["data"]]) files = extract_files(cast(Mapping[str, object], body), paths=[["data"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index a006e64705..3b80400cac 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -19,8 +19,9 @@ video_create_character_params, video_download_content_params, ) +from .._files import deepcopy_with_paths from .._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from .._utils import extract_files, path_template, maybe_transform, deepcopy_minimal, async_maybe_transform +from .._utils import extract_files, path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -104,14 +105,15 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "prompt": prompt, "input_reference": input_reference, "model": model, "seconds": seconds, "size": size, - } + }, + [["input_reference"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) # It should be noted that the actual Content-Type header that will be @@ -347,11 +349,12 @@ def create_character( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "name": name, "video": video, - } + }, + [["video"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) # It should be noted that the actual Content-Type header that will be @@ -440,11 +443,12 @@ def edit( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "prompt": prompt, "video": video, - } + }, + [["video"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) # It should be noted that the actual Content-Type header that will be @@ -493,12 +497,13 @@ def extend( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "prompt": prompt, "seconds": seconds, "video": video, - } + }, + [["video"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) # It should be noted that the actual Content-Type header that will be @@ -645,14 +650,15 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "prompt": prompt, "input_reference": input_reference, "model": model, "seconds": seconds, "size": size, - } + }, + [["input_reference"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["input_reference"]]) # It should be noted that the actual Content-Type header that will be @@ -888,11 +894,12 @@ async def create_character( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "name": name, "video": video, - } + }, + [["video"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) # It should be noted that the actual Content-Type header that will be @@ -983,11 +990,12 @@ async def edit( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "prompt": prompt, "video": video, - } + }, + [["video"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) # It should be noted that the actual Content-Type header that will be @@ -1036,12 +1044,13 @@ async def extend( timeout: Override the client-level default timeout for this request, in seconds """ - body = deepcopy_minimal( + body = deepcopy_with_paths( { "prompt": prompt, "seconds": seconds, "video": video, - } + }, + [["video"]], ) files = extract_files(cast(Mapping[str, object], body), paths=[["video"]]) # It should be noted that the actual Content-Type header that will be diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py deleted file mode 100644 index 86a2adb1a2..0000000000 --- a/tests/test_deepcopy.py +++ /dev/null @@ -1,58 +0,0 @@ -from openai._utils import deepcopy_minimal - - -def assert_different_identities(obj1: object, obj2: object) -> None: - assert obj1 == obj2 - assert id(obj1) != id(obj2) - - -def test_simple_dict() -> None: - obj1 = {"foo": "bar"} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_dict() -> None: - obj1 = {"foo": {"bar": True}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - - -def test_complex_nested_dict() -> None: - obj1 = {"foo": {"bar": [{"hello": "world"}]}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) - assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) - - -def test_simple_list() -> None: - obj1 = ["a", "b", "c"] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_list() -> None: - obj1 = ["a", [1, 2, 3]] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1[1], obj2[1]) - - -class MyObject: ... - - -def test_ignores_other_types() -> None: - # custom classes - my_obj = MyObject() - obj1 = {"foo": my_obj} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert obj1["foo"] is my_obj - - # tuples - obj3 = ("a", "b") - obj4 = deepcopy_minimal(obj3) - assert obj3 is obj4 diff --git a/tests/test_files.py b/tests/test_files.py index 15d5c6a811..3d3851f5f8 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -4,7 +4,8 @@ import pytest from dirty_equals import IsDict, IsList, IsBytes, IsTuple -from openai._files import to_httpx_files, async_to_httpx_files +from openai._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files +from openai._utils import extract_files readme_path = Path(__file__).parent.parent.joinpath("README.md") @@ -49,3 +50,99 @@ def test_string_not_allowed() -> None: "file": "foo", # type: ignore } ) + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert obj1 is not obj2 + + +class TestDeepcopyWithPaths: + def test_copies_top_level_dict(self) -> None: + original = {"file": b"data", "other": "value"} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + + def test_file_value_is_same_reference(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + assert result["file"] is file_bytes + + def test_list_popped_wholesale(self) -> None: + files = [b"f1", b"f2"] + original = {"files": files, "title": "t"} + result = deepcopy_with_paths(original, [["files", ""]]) + assert_different_identities(result, original) + result_files = result["files"] + assert isinstance(result_files, list) + assert_different_identities(result_files, files) + + def test_nested_array_path_copies_list_and_elements(self) -> None: + elem1 = {"file": b"f1", "extra": 1} + elem2 = {"file": b"f2", "extra": 2} + original = {"items": [elem1, elem2]} + result = deepcopy_with_paths(original, [["items", "", "file"]]) + assert_different_identities(result, original) + result_items = result["items"] + assert isinstance(result_items, list) + assert_different_identities(result_items, original["items"]) + assert_different_identities(result_items[0], elem1) + assert_different_identities(result_items[1], elem2) + + def test_empty_paths_returns_same_object(self) -> None: + original = {"foo": "bar"} + result = deepcopy_with_paths(original, []) + assert result is original + + def test_multiple_paths(self) -> None: + f1 = b"file1" + f2 = b"file2" + original = {"a": f1, "b": f2, "c": "unchanged"} + result = deepcopy_with_paths(original, [["a"], ["b"]]) + assert_different_identities(result, original) + assert result["a"] is f1 + assert result["b"] is f2 + assert result["c"] is original["c"] + + def test_extract_files_does_not_mutate_original_top_level(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes, "other": "value"} + + copied = deepcopy_with_paths(original, [["file"]]) + extracted = extract_files(copied, paths=[["file"]]) + + assert extracted == [("file", file_bytes)] + assert original == {"file": file_bytes, "other": "value"} + assert copied == {"other": "value"} + + def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: + file1 = b"f1" + file2 = b"f2" + original = { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + + copied = deepcopy_with_paths(original, [["items", "", "file"]]) + extracted = extract_files(copied, paths=[["items", "", "file"]]) + + assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert original == { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + assert copied == { + "items": [ + {"extra": 1}, + {"extra": 2}, + ], + "title": "example", + } From 3390091c751ee61a5f789fd6c6f3fb154ad55c39 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:50:25 +0000 Subject: [PATCH 301/408] chore(tests): bump steady to v0.22.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 886f2ffc14..04d29019fc 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.2 -- steady --version + npm exec --package=@stdy/cli@0.22.1 -- steady --version - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 57cabda6ae..7b05e44fd9 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From e5cad0908da34eb998d8c4b1d9a131f2207121cf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 13:58:48 +0000 Subject: [PATCH 302/408] chore(internal): more robust bootstrap script --- scripts/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index 953993addb..3e25ebec99 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response From cefadc522446e8f656a867250aef1300cc04fd07 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 16:21:41 +0000 Subject: [PATCH 303/408] fix(api): correct prompt_cache_retention enum value from in-memory to in_memory --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9a106e0f6d..fc7d71ecd5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c6ba619ccbf87e56b4f464230d04401fd78ad924d2606176309d19ca281af5.yml -openapi_spec_hash: 5e4f2073040a12c26ce58e86a72fe47e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bbf1d88b9b565b893dcae280be68848dc1518ecb65e009d1e94c9f0e5ecaacb2.yml +openapi_spec_hash: 0757a960c980e26cc813615d6823bfd5 config_hash: 50c98d8869a8cfdee2ab7dc664c4b6fe From e92f5609e180ffef33b640863d76ecee0d2808ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 20:39:40 +0000 Subject: [PATCH 304/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index fc7d71ecd5..8dd709a65e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bbf1d88b9b565b893dcae280be68848dc1518ecb65e009d1e94c9f0e5ecaacb2.yml openapi_spec_hash: 0757a960c980e26cc813615d6823bfd5 -config_hash: 50c98d8869a8cfdee2ab7dc664c4b6fe +config_hash: 6d772efaf4dd9acfe40be2855124118d From 1c2cef5ceacc9ccbc7ccb1d62f0463bc8fe96703 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 21:13:31 +0000 Subject: [PATCH 305/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 8dd709a65e..0908a17fe9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bbf1d88b9b565b893dcae280be68848dc1518ecb65e009d1e94c9f0e5ecaacb2.yml openapi_spec_hash: 0757a960c980e26cc813615d6823bfd5 -config_hash: 6d772efaf4dd9acfe40be2855124118d +config_hash: 1d3eb2a6fc36f206d2d67bfcf1a7080d From f9dcd90d8de1689cdf9f0ed6a9f31115562b6984 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:00:02 +0000 Subject: [PATCH 306/408] fix: use correct field name format for multipart file arrays --- src/openai/_qs.py | 8 ++----- src/openai/_types.py | 3 +++ src/openai/_utils/_utils.py | 42 ++++++++++++++++++++++++++++++------- tests/test_extract_files.py | 28 ++++++++++++++++++++----- tests/test_files.py | 2 +- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/openai/_qs.py b/src/openai/_qs.py index de8c99bc63..4127c19c62 100644 --- a/src/openai/_qs.py +++ b/src/openai/_qs.py @@ -2,17 +2,13 @@ from typing import Any, List, Tuple, Union, Mapping, TypeVar from urllib.parse import parse_qs, urlencode -from typing_extensions import Literal, get_args +from typing_extensions import get_args -from ._types import NotGiven, not_given +from ._types import NotGiven, ArrayFormat, NestedFormat, not_given from ._utils import flatten _T = TypeVar("_T") - -ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] -NestedFormat = Literal["dots", "brackets"] - PrimitiveData = Union[str, int, float, bool, None] # this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] # https://github.com/microsoft/pyright/issues/3555 diff --git a/src/openai/_types.py b/src/openai/_types.py index c55c6f808d..680a74ddb6 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -48,6 +48,9 @@ ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) _T = TypeVar("_T") +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + # Approximates httpx internal ProxiesTypes and RequestFiles types # while adding support for `PathLike` instances diff --git a/src/openai/_utils/_utils.py b/src/openai/_utils/_utils.py index e18fb09bee..9f7401ca83 100644 --- a/src/openai/_utils/_utils.py +++ b/src/openai/_utils/_utils.py @@ -18,11 +18,11 @@ ) from pathlib import Path from datetime import date, datetime -from typing_extensions import TypeGuard +from typing_extensions import TypeGuard, get_args import sniffio -from .._types import Omit, NotGiven, FileTypes, HeadersLike +from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -44,25 +44,45 @@ def extract_files( query: Mapping[str, object], *, paths: Sequence[Sequence[str]], + array_format: ArrayFormat = "brackets", ) -> list[tuple[str, FileTypes]]: """Recursively extract files from the given dictionary based on specified paths. A path may look like this ['foo', 'files', '', 'data']. + ``array_format`` controls how ```` segments contribute to the emitted + field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and + ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). + Note: this mutates the given dictionary. """ files: list[tuple[str, FileTypes]] = [] for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) + files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) return files +def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: + if array_format == "brackets": + return "[]" + if array_format == "indices": + return f"[{array_index}]" + if array_format == "repeat" or array_format == "comma": + # Both repeat the bare field name for each file part; there is no + # meaningful way to comma-join binary parts. + return "" + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + def _extract_items( obj: object, path: Sequence[str], *, index: int, flattened_key: str | None, + array_format: ArrayFormat, ) -> list[tuple[str, FileTypes]]: try: key = path[index] @@ -79,9 +99,11 @@ def _extract_items( if is_list(obj): files: list[tuple[str, FileTypes]] = [] - for entry in obj: - assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") - files.append((flattened_key + "[]", cast(FileTypes, entry))) + for array_index, entry in enumerate(obj): + suffix = _array_suffix(array_format, array_index) + emitted_key = (flattened_key + suffix) if flattened_key else suffix + assert_is_file_content(entry, key=emitted_key) + files.append((emitted_key, cast(FileTypes, entry))) return files assert_is_file_content(obj, key=flattened_key) @@ -110,6 +132,7 @@ def _extract_items( path, index=index, flattened_key=flattened_key, + array_format=array_format, ) elif is_list(obj): if key != "": @@ -121,9 +144,12 @@ def _extract_items( item, path, index=index, - flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + flattened_key=( + (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) + ), + array_format=array_format, ) - for item in obj + for array_index, item in enumerate(obj) ] ) diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index dcf85bd7c7..54490e133f 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -4,7 +4,7 @@ import pytest -from openai._types import FileTypes +from openai._types import FileTypes, ArrayFormat from openai._utils import extract_files @@ -37,10 +37,7 @@ def test_multiple_files() -> None: def test_top_level_file_array() -> None: query = {"files": [b"file one", b"file two"], "title": "hello"} - assert extract_files(query, paths=[["files", ""]]) == [ - ("files[]", b"file one"), - ("files[]", b"file two"), - ] + assert extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")] assert query == {"title": "hello"} @@ -71,3 +68,24 @@ def test_ignores_incorrect_paths( expected: list[tuple[str, FileTypes]], ) -> None: assert extract_files(query, paths=paths) == expected + + +@pytest.mark.parametrize( + "array_format,expected_top_level,expected_nested", + [ + ("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]), + ("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]), + ], +) +def test_array_format_controls_file_field_names( + array_format: ArrayFormat, + expected_top_level: list[tuple[str, FileTypes]], + expected_nested: list[tuple[str, FileTypes]], +) -> None: + top_level = {"files": [b"a", b"b"]} + assert extract_files(top_level, paths=[["files", ""]], array_format=array_format) == expected_top_level + + nested = {"items": [{"file": b"a"}, {"file": b"b"}]} + assert extract_files(nested, paths=[["items", "", "file"]], array_format=array_format) == expected_nested diff --git a/tests/test_files.py b/tests/test_files.py index 3d3851f5f8..56445fb550 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -131,7 +131,7 @@ def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: copied = deepcopy_with_paths(original, [["items", "", "file"]]) extracted = extract_files(copied, paths=[["items", "", "file"]]) - assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert [entry for _, entry in extracted] == [file1, file2] assert original == { "items": [ {"file": file1, "extra": 1}, From 166446b8522b47304e035015ca2175b7266806f5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 19:02:28 +0000 Subject: [PATCH 307/408] docs(api): add rate limit and vector store info to files create --- .stats.yml | 4 ++-- src/openai/resources/files.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0908a17fe9..ce8792b2e8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-bbf1d88b9b565b893dcae280be68848dc1518ecb65e009d1e94c9f0e5ecaacb2.yml -openapi_spec_hash: 0757a960c980e26cc813615d6823bfd5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9919ae12a2ab24389e4bcd032e986437cbc8a4b70057dda0f524976c9378f4c1.yml +openapi_spec_hash: d7bf0be60f2af5c1d2f73b2b0f4e562a config_hash: 1d3eb2a6fc36f206d2d67bfcf1a7080d diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 0cc6b046ff..c91e626b9b 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -74,7 +74,8 @@ def create( Individual files can be up to 512 MB, and each project can store up to 2.5 TB of files in total. There - is no organization-wide storage limit. + is no organization-wide storage limit. Uploads to this endpoint are rate-limited + to 2,000 files per minute per organization. - The Assistants API supports files up to 2 million tokens and of specific file types. See the @@ -89,6 +90,10 @@ def create( - The Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a specific required [format](https://platform.openai.com/docs/api-reference/batch/request-input). + - For Retrieval or `file_search` ingestion, upload files here first. If you need + to attach multiple uploaded files to the same vector store, use + [`/vector_stores/{vector_store_id}/file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) + instead of attaching them one by one. Please [contact us](https://help.openai.com/) if you need to increase these storage limits. @@ -400,7 +405,8 @@ async def create( Individual files can be up to 512 MB, and each project can store up to 2.5 TB of files in total. There - is no organization-wide storage limit. + is no organization-wide storage limit. Uploads to this endpoint are rate-limited + to 2,000 files per minute per organization. - The Assistants API supports files up to 2 million tokens and of specific file types. See the @@ -415,6 +421,10 @@ async def create( - The Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a specific required [format](https://platform.openai.com/docs/api-reference/batch/request-input). + - For Retrieval or `file_search` ingestion, upload files here first. If you need + to attach multiple uploaded files to the same vector store, use + [`/vector_stores/{vector_store_id}/file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) + instead of attaching them one by one. Please [contact us](https://help.openai.com/) if you need to increase these storage limits. From dc98fa42d167197e3a2d6be353c108df0d43b456 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 22:27:54 +0000 Subject: [PATCH 308/408] feat: support setting headers via env --- src/openai/_client.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/openai/_client.py b/src/openai/_client.py index 434f957e19..18bc80d1a0 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -24,6 +24,7 @@ from ._utils import ( is_given, is_mapping, + is_mapping_t, get_async_library, ) from ._compat import cached_property @@ -185,6 +186,15 @@ def __init__( if base_url is None: base_url = f"https://api.openai.com/v1" + custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, @@ -614,6 +624,15 @@ def __init__( if base_url is None: base_url = f"https://api.openai.com/v1" + custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, From 3380ecb943bbf293ca39a51eed55a125b0a27716 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 22:45:59 +0000 Subject: [PATCH 309/408] docs(api): update files rate limit documentation --- .stats.yml | 2 +- src/openai/resources/files.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index ce8792b2e8..158de3fccf 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9919ae12a2ab24389e4bcd032e986437cbc8a4b70057dda0f524976c9378f4c1.yml -openapi_spec_hash: d7bf0be60f2af5c1d2f73b2b0f4e562a +openapi_spec_hash: cb2783cf8428574ffd54b31e4ca87c31 config_hash: 1d3eb2a6fc36f206d2d67bfcf1a7080d diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index c91e626b9b..0cceb94b9d 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -75,7 +75,7 @@ def create( Individual files can be up to 512 MB, and each project can store up to 2.5 TB of files in total. There is no organization-wide storage limit. Uploads to this endpoint are rate-limited - to 2,000 files per minute per organization. + to 1,000 requests per minute per authenticated user. - The Assistants API supports files up to 2 million tokens and of specific file types. See the @@ -93,7 +93,9 @@ def create( - For Retrieval or `file_search` ingestion, upload files here first. If you need to attach multiple uploaded files to the same vector store, use [`/vector_stores/{vector_store_id}/file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) - instead of attaching them one by one. + instead of attaching them one by one. Vector store attachment has separate + limits from file upload, including 2,000 attached files per minute per + organization. Please [contact us](https://help.openai.com/) if you need to increase these storage limits. @@ -406,7 +408,7 @@ async def create( Individual files can be up to 512 MB, and each project can store up to 2.5 TB of files in total. There is no organization-wide storage limit. Uploads to this endpoint are rate-limited - to 2,000 files per minute per organization. + to 1,000 requests per minute per authenticated user. - The Assistants API supports files up to 2 million tokens and of specific file types. See the @@ -424,7 +426,9 @@ async def create( - For Retrieval or `file_search` ingestion, upload files here first. If you need to attach multiple uploaded files to the same vector store, use [`/vector_stores/{vector_store_id}/file_batches`](https://platform.openai.com/docs/api-reference/vector-stores-file-batches/createBatch) - instead of attaching them one by one. + instead of attaching them one by one. Vector store attachment has separate + limits from file upload, including 2,000 attached files per minute per + organization. Please [contact us](https://help.openai.com/) if you need to increase these storage limits. From 7903bcc69694ba2a41d48f8f45e5e3b07892d734 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 00:13:47 +0000 Subject: [PATCH 310/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 158de3fccf..8a3f1b60a1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9919ae12a2ab24389e4bcd032e986437cbc8a4b70057dda0f524976c9378f4c1.yml -openapi_spec_hash: cb2783cf8428574ffd54b31e4ca87c31 +openapi_spec_hash: c670ab80573e61e664f6a16f5a96d67f config_hash: 1d3eb2a6fc36f206d2d67bfcf1a7080d From b29a2501d8406bbd432af5921d54f4de4415cd4f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:58:13 +0000 Subject: [PATCH 311/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 8a3f1b60a1..b09ae1b6fd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-9919ae12a2ab24389e4bcd032e986437cbc8a4b70057dda0f524976c9378f4c1.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4f1d8a0ff9f48dc0b798df6a8ef0cfecafb1ccf14713ebb2a03dbe8ed192200e.yml openapi_spec_hash: c670ab80573e61e664f6a16f5a96d67f config_hash: 1d3eb2a6fc36f206d2d67bfcf1a7080d From 0e81ff2f45b7acf6a6de5620a66c3bd60af93b84 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 21:54:50 +0000 Subject: [PATCH 312/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b09ae1b6fd..20d21ace07 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-4f1d8a0ff9f48dc0b798df6a8ef0cfecafb1ccf14713ebb2a03dbe8ed192200e.yml -openapi_spec_hash: c670ab80573e61e664f6a16f5a96d67f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fa32d6e7d961e6a9090bfd42dada0302b1b11c40308587ea36bd46be854f33ab.yml +openapi_spec_hash: e582137699583c1fb37ddb0553a8a2d0 config_hash: 1d3eb2a6fc36f206d2d67bfcf1a7080d From 044ffcfdb749be9677a442b67f309b886a9cf3fb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 22:21:05 +0000 Subject: [PATCH 313/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 20d21ace07..58e688f32e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fa32d6e7d961e6a9090bfd42dada0302b1b11c40308587ea36bd46be854f33ab.yml openapi_spec_hash: e582137699583c1fb37ddb0553a8a2d0 -config_hash: 1d3eb2a6fc36f206d2d67bfcf1a7080d +config_hash: 2b2791e02f87210a840b88c95ccefd31 From ae9ca46f22a01a1584c15b5db8854c017a91fa46 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 01:54:01 +0000 Subject: [PATCH 314/408] feat(api): add support for Admin API Keys per endpoint --- .stats.yml | 2 +- src/openai/__init__.py | 15 + src/openai/_base_client.py | 34 +- src/openai/_client.py | 234 ++++++++---- src/openai/_models.py | 10 + src/openai/_types.py | 3 +- src/openai/lib/azure.py | 32 +- src/openai/resources/audio/speech.py | 12 +- src/openai/resources/audio/transcriptions.py | 12 +- src/openai/resources/audio/translations.py | 12 +- src/openai/resources/batches.py | 38 +- src/openai/resources/beta/assistants.py | 50 ++- src/openai/resources/beta/chatkit/sessions.py | 24 +- src/openai/resources/beta/chatkit/threads.py | 28 +- src/openai/resources/beta/threads/messages.py | 50 ++- .../resources/beta/threads/runs/runs.py | 42 ++- .../resources/beta/threads/runs/steps.py | 4 + src/openai/resources/beta/threads/threads.py | 50 ++- .../resources/chat/completions/completions.py | 50 ++- .../resources/chat/completions/messages.py | 2 + src/openai/resources/completions.py | 12 +- src/openai/resources/containers/containers.py | 38 +- .../resources/containers/files/content.py | 12 +- .../resources/containers/files/files.py | 38 +- .../resources/conversations/conversations.py | 48 ++- src/openai/resources/conversations/items.py | 18 +- src/openai/resources/embeddings.py | 2 + src/openai/resources/evals/evals.py | 50 ++- .../resources/evals/runs/output_items.py | 14 +- src/openai/resources/evals/runs/runs.py | 50 ++- src/openai/resources/files.py | 62 +++- .../resources/fine_tuning/alpha/graders.py | 24 +- .../fine_tuning/checkpoints/permissions.py | 28 +- .../resources/fine_tuning/jobs/checkpoints.py | 2 + src/openai/resources/fine_tuning/jobs/jobs.py | 64 +++- src/openai/resources/images.py | 36 +- src/openai/resources/models.py | 36 +- src/openai/resources/moderations.py | 12 +- src/openai/resources/realtime/calls.py | 60 ++- .../resources/realtime/client_secrets.py | 12 +- src/openai/resources/responses/input_items.py | 2 + .../resources/responses/input_tokens.py | 12 +- src/openai/resources/responses/responses.py | 50 ++- src/openai/resources/skills/content.py | 12 +- src/openai/resources/skills/skills.py | 50 ++- .../resources/skills/versions/content.py | 12 +- .../resources/skills/versions/versions.py | 38 +- src/openai/resources/uploads/parts.py | 12 +- src/openai/resources/uploads/uploads.py | 36 +- .../resources/vector_stores/file_batches.py | 38 +- src/openai/resources/vector_stores/files.py | 62 +++- .../resources/vector_stores/vector_stores.py | 62 +++- src/openai/resources/videos.py | 100 ++++- src/openai/types/admin/__init__.py | 3 + tests/conftest.py | 11 +- tests/test_client.py | 351 +++++++++++++++--- tests/test_module_client.py | 3 +- 57 files changed, 1807 insertions(+), 369 deletions(-) create mode 100644 src/openai/types/admin/__init__.py diff --git a/.stats.yml b/.stats.yml index 58e688f32e..613a7fb245 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fa32d6e7d961e6a9090bfd42dada0302b1b11c40308587ea36bd46be854f33ab.yml openapi_spec_hash: e582137699583c1fb37ddb0553a8a2d0 -config_hash: 2b2791e02f87210a840b88c95ccefd31 +config_hash: 5dcd82ad6d930cf0a04c3cd7bbda82cc diff --git a/src/openai/__init__.py b/src/openai/__init__.py index fc9675a8b5..5fc22da800 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -130,6 +130,8 @@ api_key: str | None = None +admin_api_key: str | None = None + organization: str | None = None project: str | None = None @@ -176,6 +178,17 @@ def api_key(self, value: str | None) -> None: # type: ignore api_key = value + @property # type: ignore + @override + def admin_api_key(self) -> str | None: + return admin_api_key + + @admin_api_key.setter # type: ignore + def admin_api_key(self, value: str | None) -> None: # type: ignore + global admin_api_key + + admin_api_key = value + @property # type: ignore @override def organization(self) -> str | None: @@ -359,6 +372,7 @@ def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction] _client = _ModuleClient( api_key=api_key, + admin_api_key=admin_api_key, organization=organization, project=project, webhook_secret=webhook_secret, @@ -368,6 +382,7 @@ def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction] default_headers=default_headers, default_query=default_query, http_client=http_client, + _enforce_credentials=False, ) return _client diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index a1d0960700..216b36aabd 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -63,7 +63,7 @@ ) from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import PYDANTIC_V1, model_copy, model_dump -from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type +from ._models import GenericModel, SecurityOptions, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, BaseAPIResponse, @@ -435,9 +435,27 @@ def _make_status_error( ) -> _exceptions.APIStatusError: raise NotImplementedError() + def _auth_headers( + self, + security: SecurityOptions, # noqa: ARG002 + ) -> dict[str, str]: + return {} + + def _auth_query( + self, + security: SecurityOptions, # noqa: ARG002 + ) -> dict[str, str]: + return {} + + def _custom_auth( + self, + security: SecurityOptions, # noqa: ARG002 + ) -> httpx.Auth | None: + return None + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: custom_headers = options.headers or {} - headers_dict = _merge_mappings(self.default_headers, custom_headers) + headers_dict = _merge_mappings({**self._auth_headers(options.security), **self.default_headers}, custom_headers) self._validate_headers(headers_dict, custom_headers) # headers are case-insensitive while dictionaries are not. @@ -509,7 +527,7 @@ def _build_request( raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") headers = self._build_headers(options, retries_taken=retries_taken) - params = _merge_mappings(self.default_query, options.params) + params = _merge_mappings({**self._auth_query(options.security), **self.default_query}, options.params) content_type = headers.get("Content-Type") files = options.files @@ -678,7 +696,6 @@ def default_headers(self) -> dict[str, str | Omit]: "Content-Type": "application/json", "User-Agent": self.user_agent, **self.platform_headers(), - **self.auth_headers, **self._custom_headers, } @@ -1006,8 +1023,9 @@ def request( self._prepare_request(request) kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + custom_auth = self._custom_auth(options.security) + if custom_auth is not None: + kwargs["auth"] = custom_auth if options.follow_redirects is not None: kwargs["follow_redirects"] = options.follow_redirects @@ -2013,6 +2031,7 @@ def make_request_options( idempotency_key: str | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, post_parser: PostParser | NotGiven = not_given, + security: SecurityOptions | None = None, synthesize_event_and_data: bool | None = None, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" @@ -2039,6 +2058,9 @@ def make_request_options( # internal options["post_parser"] = post_parser # type: ignore + if security is not None: + options["security"] = security + if synthesize_event_and_data is not None: options["synthesize_event_and_data"] = synthesize_event_and_data diff --git a/src/openai/_client.py b/src/openai/_client.py index 18bc80d1a0..3009989beb 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -13,6 +13,7 @@ from .auth import WorkloadIdentity, WorkloadIdentityAuth from ._types import ( Omit, + Headers, Timeout, NotGiven, Transport, @@ -28,10 +29,10 @@ get_async_library, ) from ._compat import cached_property -from ._models import FinalRequestOptions + from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream -from ._exceptions import OpenAIError, APIStatusError +from ._exceptions import APIStatusError from ._base_client import ( DEFAULT_MAX_RETRIES, SyncAPIClient, @@ -90,7 +91,10 @@ class OpenAI(SyncAPIClient): # client options - api_key: str +class OpenAI(SyncAPIClient): + # client options + api_key: str | None + admin_api_key: str | None workload_identity: WorkloadIdentity | None organization: str | None project: str | None @@ -108,7 +112,8 @@ class OpenAI(SyncAPIClient): def __init__( self, *, - api_key: str | None | Callable[[], str] = None, + api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, @@ -137,6 +142,7 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `OPENAI_API_KEY` + - `admin_api_key` from `OPENAI_ADMIN_KEY` - `organization` from `OPENAI_ORG_ID` - `project` from `OPENAI_PROJECT_ID` - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` @@ -155,10 +161,6 @@ def __init__( else: if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") - if api_key is None: - raise OpenAIError( - "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" - ) if callable(api_key): self.api_key = "" self._api_key_provider: Callable[[], str] | None = api_key # type: ignore[no-redef] @@ -167,6 +169,10 @@ def __init__( self._api_key_provider = None self._workload_identity_auth = None + if admin_api_key is None: + admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") + self.admin_api_key = admin_api_key + if organization is None: organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization @@ -365,35 +371,50 @@ def with_streaming_response(self) -> OpenAIWithStreamedResponse: def qs(self) -> Querystring: return Querystring(array_format="brackets") - def _refresh_api_key(self) -> None: - if self._api_key_provider: - self.api_key = self._api_key_provider() @override - def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - self._refresh_api_key() - return super()._prepare_options(options) + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + return { + **(self._bearer_auth if security.get("bearer_auth", False) else {}), + **(self._admin_api_key_auth if security.get("admin_api_key_auth", False) else {}), + } - def _send_with_auth_retry( - self, - request: httpx.Request, - *, - stream: bool, - retried: bool = False, - **kwargs: Unpack[HttpxSendArgs], - ) -> httpx.Response: - if self._workload_identity_auth: - request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" + @property + def _bearer_auth(self) -> dict[str, str]: + api_key = self.api_key - response = super()._send_request(request, stream=stream, **kwargs) + return {"Authorization": f"Bearer {api_key}"} + + @property + def _admin_api_key_auth(self) -> dict[str, str]: + admin_api_key = self.admin_api_key + if admin_api_key is None: + return {} + return {"Authorization": f"Bearer {admin_api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": "false", + "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + "OpenAI-Project": self.project if self.project is not None else Omit(), + **self._custom_headers, + } - if not retried and response.status_code == 401 and self._workload_identity_auth: - response.close() + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + return - self._workload_identity_auth.invalidate_token() - fresh_token = self._workload_identity_auth.get_token() + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) - request.headers["Authorization"] = f"Bearer {fresh_token}" + def copy( + self, + *, return self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) @@ -409,15 +430,40 @@ def _send_request( ) -> httpx.Response: return self._send_with_auth_retry(request, stream=stream, **kwargs) + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if security.get("bearer_auth", False): + headers = self._bearer_auth + if headers: + return headers + + if security.get("admin_api_key_auth", False): + return self._admin_api_key_auth + + return {} + + @property + def _bearer_auth(self) -> dict[str, str]: + api_key = self.api_key + if not api_key: + return {} + return {"Authorization": f"Bearer {api_key}"} + @property @override def auth_headers(self) -> dict[str, str]: api_key = self.api_key if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: - # if the api key is an empty string, encoding the header will fail return {} return {"Authorization": f"Bearer {api_key}"} + @property + def _admin_api_key_auth(self) -> dict[str, str]: + admin_api_key = self.admin_api_key + if admin_api_key is None: + return {} + return {"Authorization": f"Bearer {admin_api_key}"} + @property @override def default_headers(self) -> dict[str, str | Omit]: @@ -429,10 +475,20 @@ def default_headers(self) -> dict[str, str | Omit]: **self._custom_headers, } + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + def copy( self, *, api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, @@ -472,7 +528,7 @@ def copy( http_client = http_client or self._client return self.__class__( - api_key=api_key or self._api_key_provider or self.api_key, + admin_api_key=admin_api_key or self.admin_api_key, workload_identity=workload_identity or self.workload_identity, organization=organization or self.organization, project=project or self.project, @@ -528,8 +584,8 @@ def _make_status_error( class AsyncOpenAI(AsyncAPIClient): # client options - api_key: str - workload_identity: WorkloadIdentity | None + api_key: str | None + admin_api_key: str | None organization: str | None project: str | None webhook_secret: str | None @@ -546,7 +602,9 @@ class AsyncOpenAI(AsyncAPIClient): def __init__( self, *, - api_key: str | None | Callable[[], Awaitable[str]] = None, + *, + api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, @@ -575,6 +633,7 @@ def __init__( This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `OPENAI_API_KEY` + - `admin_api_key` from `OPENAI_ADMIN_KEY` - `organization` from `OPENAI_ORG_ID` - `project` from `OPENAI_PROJECT_ID` - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` @@ -593,10 +652,6 @@ def __init__( else: if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") - if api_key is None: - raise OpenAIError( - "The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable" - ) if callable(api_key): self.api_key = "" self._api_key_provider: Callable[[], Awaitable[str]] | None = api_key # type: ignore[no-redef] @@ -605,6 +660,10 @@ def __init__( self._api_key_provider = None self._workload_identity_auth = None + if admin_api_key is None: + admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") + self.admin_api_key = admin_api_key + if organization is None: organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization @@ -803,35 +862,50 @@ def with_streaming_response(self) -> AsyncOpenAIWithStreamedResponse: def qs(self) -> Querystring: return Querystring(array_format="brackets") - async def _refresh_api_key(self) -> None: - if self._api_key_provider: - self.api_key = await self._api_key_provider() @override - async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - await self._refresh_api_key() - return await super()._prepare_options(options) + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + return { + **(self._bearer_auth if security.get("bearer_auth", False) else {}), + **(self._admin_api_key_auth if security.get("admin_api_key_auth", False) else {}), + } - async def _send_with_auth_retry( - self, - request: httpx.Request, - *, - stream: bool, - retried: bool = False, - **kwargs: Unpack[HttpxSendArgs], - ) -> httpx.Response: - if self._workload_identity_auth: - request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" + @property + def _bearer_auth(self) -> dict[str, str]: + api_key = self.api_key - response = await super()._send_request(request, stream=stream, **kwargs) + return {"Authorization": f"Bearer {api_key}"} + + @property + def _admin_api_key_auth(self) -> dict[str, str]: + admin_api_key = self.admin_api_key + if admin_api_key is None: + return {} + return {"Authorization": f"Bearer {admin_api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", + "OpenAI-Organization": self.organization if self.organization is not None else Omit(), + "OpenAI-Project": self.project if self.project is not None else Omit(), + **self._custom_headers, + } - if not retried and response.status_code == 401 and self._workload_identity_auth: - await response.aclose() + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + return - self._workload_identity_auth.invalidate_token() - fresh_token = await self._workload_identity_auth.get_token_async() + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) - request.headers["Authorization"] = f"Bearer {fresh_token}" + def copy( + self, + *, return await self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) @@ -847,15 +921,40 @@ async def _send_request( ) -> httpx.Response: return await self._send_with_auth_retry(request, stream=stream, **kwargs) + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if security.get("bearer_auth", False): + headers = self._bearer_auth + if headers: + return headers + + if security.get("admin_api_key_auth", False): + return self._admin_api_key_auth + + return {} + + @property + def _bearer_auth(self) -> dict[str, str]: + api_key = self.api_key + if not api_key: + return {} + return {"Authorization": f"Bearer {api_key}"} + @property @override def auth_headers(self) -> dict[str, str]: api_key = self.api_key if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: - # if the api key is an empty string, encoding the header will fail return {} return {"Authorization": f"Bearer {api_key}"} + @property + def _admin_api_key_auth(self) -> dict[str, str]: + admin_api_key = self.admin_api_key + if admin_api_key is None: + return {} + return {"Authorization": f"Bearer {admin_api_key}"} + @property @override def default_headers(self) -> dict[str, str | Omit]: @@ -867,10 +966,20 @@ def default_headers(self) -> dict[str, str | Omit]: **self._custom_headers, } + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + def copy( self, *, api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, @@ -910,6 +1019,7 @@ def copy( http_client = http_client or self._client return self.__class__( api_key=api_key or self._api_key_provider or self.api_key, + admin_api_key=admin_api_key or self.admin_api_key, workload_identity=workload_identity or self.workload_identity, organization=organization or self.organization, project=project or self.project, diff --git a/src/openai/_models.py b/src/openai/_models.py index 810e49dfc5..5f12232437 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -832,6 +832,11 @@ def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: return RootModel[type_] # type: ignore +class SecurityOptions(TypedDict, total=False): + bearer_auth: bool + admin_api_key_auth: bool + + class FinalRequestOptionsInput(TypedDict, total=False): method: Required[str] url: Required[str] @@ -845,6 +850,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): json_data: Body extra_json: AnyMapping follow_redirects: bool + security: SecurityOptions synthesize_event_and_data: bool @@ -860,6 +866,10 @@ class FinalRequestOptions(pydantic.BaseModel): idempotency_key: Union[str, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() follow_redirects: Union[bool, None] = None + security: SecurityOptions = { + "bearer_auth": True, + "admin_api_key_auth": True, + } synthesize_event_and_data: Optional[bool] = None content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None diff --git a/src/openai/_types.py b/src/openai/_types.py index 680a74ddb6..9936b00f73 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -36,7 +36,7 @@ from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport if TYPE_CHECKING: - from ._models import BaseModel + from ._models import BaseModel, SecurityOptions from ._response import APIResponse, AsyncAPIResponse from ._legacy_response import HttpxBinaryResponseContent @@ -125,6 +125,7 @@ class RequestOptions(TypedDict, total=False): extra_json: AnyMapping idempotency_key: str follow_redirects: bool + security: SecurityOptions synthesize_event_and_data: bool diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 09fdd9507e..a7b23f8de9 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -96,6 +96,7 @@ def __init__( azure_deployment: str | None = None, api_version: str | None = None, api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -107,6 +108,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: ... @overload @@ -116,6 +118,7 @@ def __init__( azure_deployment: str | None = None, api_version: str | None = None, api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -127,6 +130,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: ... @overload @@ -136,6 +140,7 @@ def __init__( base_url: str, api_version: str | None = None, api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, organization: str | None = None, @@ -147,6 +152,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: ... def __init__( @@ -156,6 +162,7 @@ def __init__( azure_endpoint: str | None = None, azure_deployment: str | None = None, api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, # workload_identity is not functional in the Azure client workload_identity: WorkloadIdentity | None = None, # noqa: ARG002 azure_ad_token: str | None = None, @@ -171,6 +178,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: """Construct a new synchronous azure openai client instance. @@ -239,6 +247,7 @@ def __init__( super().__init__( api_key=api_key, + admin_api_key=admin_api_key, organization=organization, project=project, webhook_secret=webhook_secret, @@ -250,6 +259,7 @@ def __init__( http_client=http_client, websocket_base_url=websocket_base_url, _strict_response_validation=_strict_response_validation, + _enforce_credentials=_enforce_credentials, ) self._api_version = api_version self._azure_ad_token = azure_ad_token @@ -262,6 +272,7 @@ def copy( self, *, api_key: str | Callable[[], str] | None = None, + admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, @@ -278,6 +289,7 @@ def copy( set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ @@ -285,6 +297,7 @@ def copy( """ return super().copy( api_key=api_key, + admin_api_key=admin_api_key, workload_identity=workload_identity, organization=organization, project=project, @@ -298,6 +311,7 @@ def copy( set_default_headers=set_default_headers, default_query=default_query, set_default_query=set_default_query, + _enforce_credentials=_enforce_credentials, _extra_kwargs={ "api_version": api_version or self._api_version, "azure_ad_token": azure_ad_token or self._azure_ad_token, @@ -334,7 +348,7 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: if azure_ad_token is not None: if headers.get("Authorization") is None: headers["Authorization"] = f"Bearer {azure_ad_token}" - elif self.api_key is not API_KEY_SENTINEL: + elif self.api_key is not None and self.api_key is not API_KEY_SENTINEL: if headers.get("api-key") is None: headers["api-key"] = self.api_key else: @@ -378,6 +392,7 @@ def __init__( azure_deployment: str | None = None, api_version: str | None = None, api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -390,6 +405,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: ... @overload @@ -399,6 +415,7 @@ def __init__( azure_deployment: str | None = None, api_version: str | None = None, api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -411,6 +428,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: ... @overload @@ -420,6 +438,7 @@ def __init__( base_url: str, api_version: str | None = None, api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, azure_ad_token: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, organization: str | None = None, @@ -432,6 +451,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: ... def __init__( @@ -441,6 +461,7 @@ def __init__( azure_deployment: str | None = None, api_version: str | None = None, api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, # workload_identity is not functional in the Azure client workload_identity: WorkloadIdentity | None = None, # noqa: ARG002 azure_ad_token: str | None = None, @@ -456,6 +477,7 @@ def __init__( default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: """Construct a new asynchronous azure openai client instance. @@ -524,6 +546,7 @@ def __init__( super().__init__( api_key=api_key, + admin_api_key=admin_api_key, organization=organization, project=project, webhook_secret=webhook_secret, @@ -535,6 +558,7 @@ def __init__( http_client=http_client, websocket_base_url=websocket_base_url, _strict_response_validation=_strict_response_validation, + _enforce_credentials=_enforce_credentials, ) self._api_version = api_version self._azure_ad_token = azure_ad_token @@ -547,6 +571,7 @@ def copy( self, *, api_key: str | Callable[[], Awaitable[str]] | None = None, + admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, organization: str | None = None, project: str | None = None, @@ -563,6 +588,7 @@ def copy( set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ @@ -570,6 +596,7 @@ def copy( """ return super().copy( api_key=api_key, + admin_api_key=admin_api_key, workload_identity=workload_identity, organization=organization, project=project, @@ -583,6 +610,7 @@ def copy( set_default_headers=set_default_headers, default_query=default_query, set_default_query=set_default_query, + _enforce_credentials=_enforce_credentials, _extra_kwargs={ "api_version": api_version or self._api_version, "azure_ad_token": azure_ad_token or self._azure_ad_token, @@ -621,7 +649,7 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp if azure_ad_token is not None: if headers.get("Authorization") is None: headers["Authorization"] = f"Bearer {azure_ad_token}" - elif self.api_key is not API_KEY_SENTINEL: + elif self.api_key is not None and self.api_key is not API_KEY_SENTINEL: if headers.get("api-key") is None: headers["api-key"] = self.api_key else: diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 80dbb44077..3c05d690dd 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -119,7 +119,11 @@ def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -219,7 +223,11 @@ async def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) diff --git a/src/openai/resources/audio/transcriptions.py b/src/openai/resources/audio/transcriptions.py index e5f415f278..ab2480ef11 100644 --- a/src/openai/resources/audio/transcriptions.py +++ b/src/openai/resources/audio/transcriptions.py @@ -492,7 +492,11 @@ def create( ), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_get_response_format_type(response_format), stream=stream or False, @@ -947,7 +951,11 @@ async def create( ), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_get_response_format_type(response_format), stream=stream or False, diff --git a/src/openai/resources/audio/translations.py b/src/openai/resources/audio/translations.py index d6df25c1b7..d0b5738045 100644 --- a/src/openai/resources/audio/translations.py +++ b/src/openai/resources/audio/translations.py @@ -167,7 +167,11 @@ def create( body=maybe_transform(body, translation_create_params.TranslationCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_get_response_format_type(response_format), ) @@ -313,7 +317,11 @@ async def create( body=await async_maybe_transform(body, translation_create_params.TranslationCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_get_response_format_type(response_format), ) diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py index 6cdb50c280..e7146ef35f 100644 --- a/src/openai/resources/batches.py +++ b/src/openai/resources/batches.py @@ -123,7 +123,11 @@ def create( batch_create_params.BatchCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Batch, ) @@ -156,7 +160,11 @@ def retrieve( return self._get( path_template("/batches/{batch_id}", batch_id=batch_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Batch, ) @@ -209,6 +217,7 @@ def list( }, batch_list_params.BatchListParams, ), + security={"bearer_auth": True}, ), model=Batch, ) @@ -244,7 +253,11 @@ def cancel( return self._post( path_template("/batches/{batch_id}/cancel", batch_id=batch_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Batch, ) @@ -351,7 +364,11 @@ async def create( batch_create_params.BatchCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Batch, ) @@ -384,7 +401,11 @@ async def retrieve( return await self._get( path_template("/batches/{batch_id}", batch_id=batch_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Batch, ) @@ -437,6 +458,7 @@ def list( }, batch_list_params.BatchListParams, ), + security={"bearer_auth": True}, ), model=Batch, ) @@ -472,7 +494,11 @@ async def cancel( return await self._post( path_template("/batches/{batch_id}/cancel", batch_id=batch_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Batch, ) diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 7ea8a918ca..6301082fea 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -182,7 +182,11 @@ def create( assistant_create_params.AssistantCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Assistant, ) @@ -217,7 +221,11 @@ def retrieve( return self._get( path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Assistant, ) @@ -401,7 +409,11 @@ def update( assistant_update_params.AssistantUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Assistant, ) @@ -468,6 +480,7 @@ def list( }, assistant_list_params.AssistantListParams, ), + security={"bearer_auth": True}, ), model=Assistant, ) @@ -502,7 +515,11 @@ def delete( return self._delete( path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=AssistantDeleted, ) @@ -658,7 +675,11 @@ async def create( assistant_create_params.AssistantCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Assistant, ) @@ -693,7 +714,11 @@ async def retrieve( return await self._get( path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Assistant, ) @@ -877,7 +902,11 @@ async def update( assistant_update_params.AssistantUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Assistant, ) @@ -944,6 +973,7 @@ def list( }, assistant_list_params.AssistantListParams, ), + security={"bearer_auth": True}, ), model=Assistant, ) @@ -978,7 +1008,11 @@ async def delete( return await self._delete( path_template("/assistants/{assistant_id}", assistant_id=assistant_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=AssistantDeleted, ) diff --git a/src/openai/resources/beta/chatkit/sessions.py b/src/openai/resources/beta/chatkit/sessions.py index 6e95fd65fb..9049b06a40 100644 --- a/src/openai/resources/beta/chatkit/sessions.py +++ b/src/openai/resources/beta/chatkit/sessions.py @@ -100,7 +100,11 @@ def create( session_create_params.SessionCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatSession, ) @@ -136,7 +140,11 @@ def cancel( return self._post( path_template("/chatkit/sessions/{session_id}/cancel", session_id=session_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatSession, ) @@ -215,7 +223,11 @@ async def create( session_create_params.SessionCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatSession, ) @@ -251,7 +263,11 @@ async def cancel( return await self._post( path_template("/chatkit/sessions/{session_id}/cancel", session_id=session_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatSession, ) diff --git a/src/openai/resources/beta/chatkit/threads.py b/src/openai/resources/beta/chatkit/threads.py index 16e0e11a0a..05ebf0fb87 100644 --- a/src/openai/resources/beta/chatkit/threads.py +++ b/src/openai/resources/beta/chatkit/threads.py @@ -72,7 +72,11 @@ def retrieve( return self._get( path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatKitThread, ) @@ -136,6 +140,7 @@ def list( }, thread_list_params.ThreadListParams, ), + security={"bearer_auth": True}, ), model=ChatKitThread, ) @@ -169,7 +174,11 @@ def delete( return self._delete( path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ThreadDeleteResponse, ) @@ -231,6 +240,7 @@ def list_items( }, thread_list_items_params.ThreadListItemsParams, ), + security={"bearer_auth": True}, ), model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system ) @@ -285,7 +295,11 @@ async def retrieve( return await self._get( path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatKitThread, ) @@ -349,6 +363,7 @@ def list( }, thread_list_params.ThreadListParams, ), + security={"bearer_auth": True}, ), model=ChatKitThread, ) @@ -382,7 +397,11 @@ async def delete( return await self._delete( path_template("/chatkit/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ThreadDeleteResponse, ) @@ -444,6 +463,7 @@ def list_items( }, thread_list_items_params.ThreadListItemsParams, ), + security={"bearer_auth": True}, ), model=cast(Any, Data), # Union types cannot be passed in as arguments in the type system ) diff --git a/src/openai/resources/beta/threads/messages.py b/src/openai/resources/beta/threads/messages.py index 95b750d4e4..e57e783577 100644 --- a/src/openai/resources/beta/threads/messages.py +++ b/src/openai/resources/beta/threads/messages.py @@ -112,7 +112,11 @@ def create( message_create_params.MessageCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Message, ) @@ -150,7 +154,11 @@ def retrieve( return self._get( path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Message, ) @@ -197,7 +205,11 @@ def update( path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Message, ) @@ -270,6 +282,7 @@ def list( }, message_list_params.MessageListParams, ), + security={"bearer_auth": True}, ), model=Message, ) @@ -307,7 +320,11 @@ def delete( return self._delete( path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=MessageDeleted, ) @@ -397,7 +414,11 @@ async def create( message_create_params.MessageCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Message, ) @@ -435,7 +456,11 @@ async def retrieve( return await self._get( path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Message, ) @@ -482,7 +507,11 @@ async def update( path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), body=await async_maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Message, ) @@ -555,6 +584,7 @@ def list( }, message_list_params.MessageListParams, ), + security={"bearer_auth": True}, ), model=Message, ) @@ -592,7 +622,11 @@ async def delete( return await self._delete( path_template("/threads/{thread_id}/messages/{message_id}", thread_id=thread_id, message_id=message_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=MessageDeleted, ) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 882e88dfa6..263bc787c2 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -624,6 +624,7 @@ def create( extra_body=extra_body, timeout=timeout, query=maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, synthesize_event_and_data=True, ), cast_to=Run, @@ -664,7 +665,11 @@ def retrieve( return self._get( path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, ) @@ -711,7 +716,11 @@ def update( path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, ) @@ -780,6 +789,7 @@ def list( }, run_list_params.RunListParams, ), + security={"bearer_auth": True}, ), model=Run, ) @@ -817,7 +827,11 @@ def cancel( return self._post( path_template("/threads/{thread_id}/runs/{run_id}/cancel", thread_id=thread_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, ) @@ -1377,6 +1391,7 @@ def submit_tool_outputs( extra_query=extra_query, extra_body=extra_body, timeout=timeout, + security={"bearer_auth": True}, synthesize_event_and_data=True, ), cast_to=Run, @@ -2087,6 +2102,7 @@ async def create( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, synthesize_event_and_data=True, ), cast_to=Run, @@ -2127,7 +2143,11 @@ async def retrieve( return await self._get( path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, ) @@ -2174,7 +2194,11 @@ async def update( path_template("/threads/{thread_id}/runs/{run_id}", thread_id=thread_id, run_id=run_id), body=await async_maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, ) @@ -2243,6 +2267,7 @@ def list( }, run_list_params.RunListParams, ), + security={"bearer_auth": True}, ), model=Run, ) @@ -2280,7 +2305,11 @@ async def cancel( return await self._post( path_template("/threads/{thread_id}/runs/{run_id}/cancel", thread_id=thread_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, ) @@ -2839,6 +2868,7 @@ async def submit_tool_outputs( extra_query=extra_query, extra_body=extra_body, timeout=timeout, + security={"bearer_auth": True}, synthesize_event_and_data=True, ), cast_to=Run, diff --git a/src/openai/resources/beta/threads/runs/steps.py b/src/openai/resources/beta/threads/runs/steps.py index 9a6402b263..a251d27bf5 100644 --- a/src/openai/resources/beta/threads/runs/steps.py +++ b/src/openai/resources/beta/threads/runs/steps.py @@ -100,6 +100,7 @@ def retrieve( extra_body=extra_body, timeout=timeout, query=maybe_transform({"include": include}, step_retrieve_params.StepRetrieveParams), + security={"bearer_auth": True}, ), cast_to=RunStep, ) @@ -181,6 +182,7 @@ def list( }, step_list_params.StepListParams, ), + security={"bearer_auth": True}, ), model=RunStep, ) @@ -263,6 +265,7 @@ async def retrieve( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"include": include}, step_retrieve_params.StepRetrieveParams), + security={"bearer_auth": True}, ), cast_to=RunStep, ) @@ -344,6 +347,7 @@ def list( }, step_list_params.StepListParams, ), + security={"bearer_auth": True}, ), model=RunStep, ) diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index 4b0f18fe47..dec6f31a38 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -144,7 +144,11 @@ def create( thread_create_params.ThreadCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Thread, ) @@ -179,7 +183,11 @@ def retrieve( return self._get( path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Thread, ) @@ -235,7 +243,11 @@ def update( thread_update_params.ThreadUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Thread, ) @@ -270,7 +282,11 @@ def delete( return self._delete( path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ThreadDeleted, ) @@ -737,6 +753,7 @@ def create_and_run( extra_query=extra_query, extra_body=extra_body, timeout=timeout, + security={"bearer_auth": True}, synthesize_event_and_data=True, ), cast_to=Run, @@ -1010,7 +1027,11 @@ async def create( thread_create_params.ThreadCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Thread, ) @@ -1045,7 +1066,11 @@ async def retrieve( return await self._get( path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Thread, ) @@ -1101,7 +1126,11 @@ async def update( thread_update_params.ThreadUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Thread, ) @@ -1136,7 +1165,11 @@ async def delete( return await self._delete( path_template("/threads/{thread_id}", thread_id=thread_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ThreadDeleted, ) @@ -1603,6 +1636,7 @@ async def create_and_run( extra_query=extra_query, extra_body=extra_body, timeout=timeout, + security={"bearer_auth": True}, synthesize_event_and_data=True, ), cast_to=Run, diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 8b4fc12ae9..4158f032ee 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -1253,7 +1253,11 @@ def create( else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletion, stream=stream or False, @@ -1290,7 +1294,11 @@ def retrieve( return self._get( path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletion, ) @@ -1335,7 +1343,11 @@ def update( path_template("/chat/completions/{completion_id}", completion_id=completion_id), body=maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletion, ) @@ -1401,6 +1413,7 @@ def list( }, completion_list_params.CompletionListParams, ), + security={"bearer_auth": True}, ), model=ChatCompletion, ) @@ -1435,7 +1448,11 @@ def delete( return self._delete( path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletionDeleted, ) @@ -2756,7 +2773,11 @@ async def create( else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletion, stream=stream or False, @@ -2793,7 +2814,11 @@ async def retrieve( return await self._get( path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletion, ) @@ -2838,7 +2863,11 @@ async def update( path_template("/chat/completions/{completion_id}", completion_id=completion_id), body=await async_maybe_transform({"metadata": metadata}, completion_update_params.CompletionUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletion, ) @@ -2904,6 +2933,7 @@ def list( }, completion_list_params.CompletionListParams, ), + security={"bearer_auth": True}, ), model=ChatCompletion, ) @@ -2938,7 +2968,11 @@ async def delete( return await self._delete( path_template("/chat/completions/{completion_id}", completion_id=completion_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ChatCompletionDeleted, ) diff --git a/src/openai/resources/chat/completions/messages.py b/src/openai/resources/chat/completions/messages.py index ffbff566db..05dd23735a 100644 --- a/src/openai/resources/chat/completions/messages.py +++ b/src/openai/resources/chat/completions/messages.py @@ -97,6 +97,7 @@ def list( }, message_list_params.MessageListParams, ), + security={"bearer_auth": True}, ), model=ChatCompletionStoreMessage, ) @@ -179,6 +180,7 @@ def list( }, message_list_params.MessageListParams, ), + security={"bearer_auth": True}, ), model=ChatCompletionStoreMessage, ) diff --git a/src/openai/resources/completions.py b/src/openai/resources/completions.py index 4c9e266787..d2eb646a66 100644 --- a/src/openai/resources/completions.py +++ b/src/openai/resources/completions.py @@ -579,7 +579,11 @@ def create( else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Completion, stream=stream or False, @@ -1142,7 +1146,11 @@ async def create( else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Completion, stream=stream or False, diff --git a/src/openai/resources/containers/containers.py b/src/openai/resources/containers/containers.py index f6b8c33c75..7588ad7240 100644 --- a/src/openai/resources/containers/containers.py +++ b/src/openai/resources/containers/containers.py @@ -109,7 +109,11 @@ def create( container_create_params.ContainerCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ContainerCreateResponse, ) @@ -142,7 +146,11 @@ def retrieve( return self._get( path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ContainerRetrieveResponse, ) @@ -204,6 +212,7 @@ def list( }, container_list_params.ContainerListParams, ), + security={"bearer_auth": True}, ), model=ContainerListResponse, ) @@ -237,7 +246,11 @@ def delete( return self._delete( path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -321,7 +334,11 @@ async def create( container_create_params.ContainerCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ContainerCreateResponse, ) @@ -354,7 +371,11 @@ async def retrieve( return await self._get( path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ContainerRetrieveResponse, ) @@ -416,6 +437,7 @@ def list( }, container_list_params.ContainerListParams, ), + security={"bearer_auth": True}, ), model=ContainerListResponse, ) @@ -449,7 +471,11 @@ async def delete( return await self._delete( path_template("/containers/{container_id}", container_id=container_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) diff --git a/src/openai/resources/containers/files/content.py b/src/openai/resources/containers/files/content.py index eb915b9c13..7df5e4cf2d 100644 --- a/src/openai/resources/containers/files/content.py +++ b/src/openai/resources/containers/files/content.py @@ -74,7 +74,11 @@ def retrieve( "/containers/{container_id}/files/{file_id}/content", container_id=container_id, file_id=file_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -134,7 +138,11 @@ async def retrieve( "/containers/{container_id}/files/{file_id}/content", container_id=container_id, file_id=file_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) diff --git a/src/openai/resources/containers/files/files.py b/src/openai/resources/containers/files/files.py index 80e0d61e0a..736b1fb75b 100644 --- a/src/openai/resources/containers/files/files.py +++ b/src/openai/resources/containers/files/files.py @@ -108,7 +108,11 @@ def create( body=maybe_transform(body, file_create_params.FileCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileCreateResponse, ) @@ -144,7 +148,11 @@ def retrieve( return self._get( path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileRetrieveResponse, ) @@ -205,6 +213,7 @@ def list( }, file_list_params.FileListParams, ), + security={"bearer_auth": True}, ), model=FileListResponse, ) @@ -241,7 +250,11 @@ def delete( return self._delete( path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -323,7 +336,11 @@ async def create( body=await async_maybe_transform(body, file_create_params.FileCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileCreateResponse, ) @@ -359,7 +376,11 @@ async def retrieve( return await self._get( path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileRetrieveResponse, ) @@ -420,6 +441,7 @@ def list( }, file_list_params.FileListParams, ), + security={"bearer_auth": True}, ), model=FileListResponse, ) @@ -456,7 +478,11 @@ async def delete( return await self._delete( path_template("/containers/{container_id}/files/{file_id}", container_id=container_id, file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) diff --git a/src/openai/resources/conversations/conversations.py b/src/openai/resources/conversations/conversations.py index d349f38546..bdc8e1d637 100644 --- a/src/openai/resources/conversations/conversations.py +++ b/src/openai/resources/conversations/conversations.py @@ -101,7 +101,11 @@ def create( conversation_create_params.ConversationCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) @@ -134,7 +138,11 @@ def retrieve( return self._get( path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) @@ -176,7 +184,11 @@ def update( path_template("/conversations/{conversation_id}", conversation_id=conversation_id), body=maybe_transform({"metadata": metadata}, conversation_update_params.ConversationUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) @@ -210,7 +222,11 @@ def delete( return self._delete( path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ConversationDeletedResource, ) @@ -287,7 +303,11 @@ async def create( conversation_create_params.ConversationCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) @@ -320,7 +340,11 @@ async def retrieve( return await self._get( path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) @@ -364,7 +388,11 @@ async def update( {"metadata": metadata}, conversation_update_params.ConversationUpdateParams ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) @@ -398,7 +426,11 @@ async def delete( return await self._delete( path_template("/conversations/{conversation_id}", conversation_id=conversation_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ConversationDeletedResource, ) diff --git a/src/openai/resources/conversations/items.py b/src/openai/resources/conversations/items.py index 7d7c9a4aba..01cdfe0804 100644 --- a/src/openai/resources/conversations/items.py +++ b/src/openai/resources/conversations/items.py @@ -89,6 +89,7 @@ def create( extra_body=extra_body, timeout=timeout, query=maybe_transform({"include": include}, item_create_params.ItemCreateParams), + security={"bearer_auth": True}, ), cast_to=ConversationItemList, ) @@ -138,6 +139,7 @@ def retrieve( extra_body=extra_body, timeout=timeout, query=maybe_transform({"include": include}, item_retrieve_params.ItemRetrieveParams), + security={"bearer_auth": True}, ), cast_to=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system ), @@ -218,6 +220,7 @@ def list( }, item_list_params.ItemListParams, ), + security={"bearer_auth": True}, ), model=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system ) @@ -255,7 +258,11 @@ def delete( "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) @@ -325,6 +332,7 @@ async def create( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"include": include}, item_create_params.ItemCreateParams), + security={"bearer_auth": True}, ), cast_to=ConversationItemList, ) @@ -374,6 +382,7 @@ async def retrieve( extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"include": include}, item_retrieve_params.ItemRetrieveParams), + security={"bearer_auth": True}, ), cast_to=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system ), @@ -454,6 +463,7 @@ def list( }, item_list_params.ItemListParams, ), + security={"bearer_auth": True}, ), model=cast(Any, ConversationItem), # Union types cannot be passed in as arguments in the type system ) @@ -491,7 +501,11 @@ async def delete( "/conversations/{conversation_id}/items/{item_id}", conversation_id=conversation_id, item_id=item_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Conversation, ) diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 86eb949a40..6f44ebac96 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -259,12 +259,14 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: return await self._post( "/embeddings", body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams), + options=make_request_options( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, post_parser=parser, + security={"bearer_auth": True}, ), cast_to=CreateEmbeddingResponse, ) diff --git a/src/openai/resources/evals/evals.py b/src/openai/resources/evals/evals.py index 6acd669a2c..a23c9cdef2 100644 --- a/src/openai/resources/evals/evals.py +++ b/src/openai/resources/evals/evals.py @@ -121,7 +121,11 @@ def create( eval_create_params.EvalCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalCreateResponse, ) @@ -154,7 +158,11 @@ def retrieve( return self._get( path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalRetrieveResponse, ) @@ -205,7 +213,11 @@ def update( eval_update_params.EvalUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalUpdateResponse, ) @@ -263,6 +275,7 @@ def list( }, eval_list_params.EvalListParams, ), + security={"bearer_auth": True}, ), model=EvalListResponse, ) @@ -295,7 +308,11 @@ def delete( return self._delete( path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalDeleteResponse, ) @@ -388,7 +405,11 @@ async def create( eval_create_params.EvalCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalCreateResponse, ) @@ -421,7 +442,11 @@ async def retrieve( return await self._get( path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalRetrieveResponse, ) @@ -472,7 +497,11 @@ async def update( eval_update_params.EvalUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalUpdateResponse, ) @@ -530,6 +559,7 @@ def list( }, eval_list_params.EvalListParams, ), + security={"bearer_auth": True}, ), model=EvalListResponse, ) @@ -562,7 +592,11 @@ async def delete( return await self._delete( path_template("/evals/{eval_id}", eval_id=eval_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=EvalDeleteResponse, ) diff --git a/src/openai/resources/evals/runs/output_items.py b/src/openai/resources/evals/runs/output_items.py index 7a498a7ebf..2f884dd876 100644 --- a/src/openai/resources/evals/runs/output_items.py +++ b/src/openai/resources/evals/runs/output_items.py @@ -82,7 +82,11 @@ def retrieve( output_item_id=output_item_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=OutputItemRetrieveResponse, ) @@ -146,6 +150,7 @@ def list( }, output_item_list_params.OutputItemListParams, ), + security={"bearer_auth": True}, ), model=OutputItemListResponse, ) @@ -212,7 +217,11 @@ async def retrieve( output_item_id=output_item_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=OutputItemRetrieveResponse, ) @@ -276,6 +285,7 @@ def list( }, output_item_list_params.OutputItemListParams, ), + security={"bearer_auth": True}, ), model=OutputItemListResponse, ) diff --git a/src/openai/resources/evals/runs/runs.py b/src/openai/resources/evals/runs/runs.py index 152ce9cb77..ffba38db2e 100644 --- a/src/openai/resources/evals/runs/runs.py +++ b/src/openai/resources/evals/runs/runs.py @@ -113,7 +113,11 @@ def create( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunCreateResponse, ) @@ -149,7 +153,11 @@ def retrieve( return self._get( path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunRetrieveResponse, ) @@ -210,6 +218,7 @@ def list( }, run_list_params.RunListParams, ), + security={"bearer_auth": True}, ), model=RunListResponse, ) @@ -245,7 +254,11 @@ def delete( return self._delete( path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunDeleteResponse, ) @@ -281,7 +294,11 @@ def cancel( return self._post( path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunCancelResponse, ) @@ -366,7 +383,11 @@ async def create( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunCreateResponse, ) @@ -402,7 +423,11 @@ async def retrieve( return await self._get( path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunRetrieveResponse, ) @@ -463,6 +488,7 @@ def list( }, run_list_params.RunListParams, ), + security={"bearer_auth": True}, ), model=RunListResponse, ) @@ -498,7 +524,11 @@ async def delete( return await self._delete( path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunDeleteResponse, ) @@ -534,7 +564,11 @@ async def cancel( return await self._post( path_template("/evals/{eval_id}/runs/{run_id}", eval_id=eval_id, run_id=run_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=RunCancelResponse, ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index 0cceb94b9d..868742f0f0 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -142,7 +142,11 @@ def create( body=maybe_transform(body, file_create_params.FileCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileObject, ) @@ -175,7 +179,11 @@ def retrieve( return self._get( path_template("/files/{file_id}", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileObject, ) @@ -237,6 +245,7 @@ def list( }, file_list_params.FileListParams, ), + security={"bearer_auth": True}, ), model=FileObject, ) @@ -269,7 +278,11 @@ def delete( return self._delete( path_template("/files/{file_id}", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileDeleted, ) @@ -303,7 +316,11 @@ def content( return self._get( path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -337,7 +354,11 @@ def retrieve_content( return self._get( path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=str, ) @@ -475,7 +496,11 @@ async def create( body=await async_maybe_transform(body, file_create_params.FileCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileObject, ) @@ -508,7 +533,11 @@ async def retrieve( return await self._get( path_template("/files/{file_id}", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileObject, ) @@ -570,6 +599,7 @@ def list( }, file_list_params.FileListParams, ), + security={"bearer_auth": True}, ), model=FileObject, ) @@ -602,7 +632,11 @@ async def delete( return await self._delete( path_template("/files/{file_id}", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FileDeleted, ) @@ -636,7 +670,11 @@ async def content( return await self._get( path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -670,7 +708,11 @@ async def retrieve_content( return await self._get( path_template("/files/{file_id}/content", file_id=file_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=str, ) diff --git a/src/openai/resources/fine_tuning/alpha/graders.py b/src/openai/resources/fine_tuning/alpha/graders.py index e5d5dea5de..51c491b91f 100644 --- a/src/openai/resources/fine_tuning/alpha/graders.py +++ b/src/openai/resources/fine_tuning/alpha/graders.py @@ -88,7 +88,11 @@ def run( grader_run_params.GraderRunParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=GraderRunResponse, ) @@ -122,7 +126,11 @@ def validate( "/fine_tuning/alpha/graders/validate", body=maybe_transform({"grader": grader}, grader_validate_params.GraderValidateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=GraderValidateResponse, ) @@ -198,7 +206,11 @@ async def run( grader_run_params.GraderRunParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=GraderRunResponse, ) @@ -232,7 +244,11 @@ async def validate( "/fine_tuning/alpha/graders/validate", body=await async_maybe_transform({"grader": grader}, grader_validate_params.GraderValidateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=GraderValidateResponse, ) diff --git a/src/openai/resources/fine_tuning/checkpoints/permissions.py b/src/openai/resources/fine_tuning/checkpoints/permissions.py index 15184e130b..49687c15cd 100644 --- a/src/openai/resources/fine_tuning/checkpoints/permissions.py +++ b/src/openai/resources/fine_tuning/checkpoints/permissions.py @@ -91,7 +91,11 @@ def create( page=SyncPage[PermissionCreateResponse], body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=PermissionCreateResponse, method="post", @@ -159,6 +163,7 @@ def retrieve( }, permission_retrieve_params.PermissionRetrieveParams, ), + security={"bearer_auth": True}, ), cast_to=PermissionRetrieveResponse, ) @@ -225,6 +230,7 @@ def list( }, permission_list_params.PermissionListParams, ), + security={"bearer_auth": True}, ), model=PermissionListResponse, ) @@ -269,7 +275,11 @@ def delete( permission_id=permission_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=PermissionDeleteResponse, ) @@ -338,7 +348,11 @@ def create( page=AsyncPage[PermissionCreateResponse], body=maybe_transform({"project_ids": project_ids}, permission_create_params.PermissionCreateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=PermissionCreateResponse, method="post", @@ -406,6 +420,7 @@ async def retrieve( }, permission_retrieve_params.PermissionRetrieveParams, ), + security={"bearer_auth": True}, ), cast_to=PermissionRetrieveResponse, ) @@ -472,6 +487,7 @@ def list( }, permission_list_params.PermissionListParams, ), + security={"bearer_auth": True}, ), model=PermissionListResponse, ) @@ -516,7 +532,11 @@ async def delete( permission_id=permission_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=PermissionDeleteResponse, ) diff --git a/src/openai/resources/fine_tuning/jobs/checkpoints.py b/src/openai/resources/fine_tuning/jobs/checkpoints.py index 0f91a6218a..b679372386 100644 --- a/src/openai/resources/fine_tuning/jobs/checkpoints.py +++ b/src/openai/resources/fine_tuning/jobs/checkpoints.py @@ -89,6 +89,7 @@ def list( }, checkpoint_list_params.CheckpointListParams, ), + security={"bearer_auth": True}, ), model=FineTuningJobCheckpoint, ) @@ -162,6 +163,7 @@ def list( }, checkpoint_list_params.CheckpointListParams, ), + security={"bearer_auth": True}, ), model=FineTuningJobCheckpoint, ) diff --git a/src/openai/resources/fine_tuning/jobs/jobs.py b/src/openai/resources/fine_tuning/jobs/jobs.py index a948b10349..e7e8903a84 100644 --- a/src/openai/resources/fine_tuning/jobs/jobs.py +++ b/src/openai/resources/fine_tuning/jobs/jobs.py @@ -175,7 +175,11 @@ def create( job_create_params.JobCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -210,7 +214,11 @@ def retrieve( return self._get( path_template("/fine_tuning/jobs/{fine_tuning_job_id}", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -263,6 +271,7 @@ def list( }, job_list_params.JobListParams, ), + security={"bearer_auth": True}, ), model=FineTuningJob, ) @@ -295,7 +304,11 @@ def cancel( return self._post( path_template("/fine_tuning/jobs/{fine_tuning_job_id}/cancel", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -346,6 +359,7 @@ def list_events( }, job_list_events_params.JobListEventsParams, ), + security={"bearer_auth": True}, ), model=FineTuningJobEvent, ) @@ -378,7 +392,11 @@ def pause( return self._post( path_template("/fine_tuning/jobs/{fine_tuning_job_id}/pause", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -411,7 +429,11 @@ def resume( return self._post( path_template("/fine_tuning/jobs/{fine_tuning_job_id}/resume", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -558,7 +580,11 @@ async def create( job_create_params.JobCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -593,7 +619,11 @@ async def retrieve( return await self._get( path_template("/fine_tuning/jobs/{fine_tuning_job_id}", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -646,6 +676,7 @@ def list( }, job_list_params.JobListParams, ), + security={"bearer_auth": True}, ), model=FineTuningJob, ) @@ -678,7 +709,11 @@ async def cancel( return await self._post( path_template("/fine_tuning/jobs/{fine_tuning_job_id}/cancel", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -729,6 +764,7 @@ def list_events( }, job_list_events_params.JobListEventsParams, ), + security={"bearer_auth": True}, ), model=FineTuningJobEvent, ) @@ -761,7 +797,11 @@ async def pause( return await self._post( path_template("/fine_tuning/jobs/{fine_tuning_job_id}/pause", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) @@ -794,7 +834,11 @@ async def resume( return await self._post( path_template("/fine_tuning/jobs/{fine_tuning_job_id}/resume", fine_tuning_job_id=fine_tuning_job_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=FineTuningJob, ) diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 2555b6c5db..ec4ca23aa2 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -116,7 +116,11 @@ def create_variation( body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ImagesResponse, ) @@ -519,7 +523,11 @@ def edit( ), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ImagesResponse, stream=stream or False, @@ -911,7 +919,11 @@ def generate( else image_generate_params.ImageGenerateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ImagesResponse, stream=stream or False, @@ -1010,7 +1022,11 @@ async def create_variation( body=await async_maybe_transform(body, image_create_variation_params.ImageCreateVariationParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ImagesResponse, ) @@ -1413,7 +1429,11 @@ async def edit( ), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ImagesResponse, stream=stream or False, @@ -1805,7 +1825,11 @@ async def generate( else image_generate_params.ImageGenerateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ImagesResponse, stream=stream or False, diff --git a/src/openai/resources/models.py b/src/openai/resources/models.py index a1fe0d395e..a68fd83360 100644 --- a/src/openai/resources/models.py +++ b/src/openai/resources/models.py @@ -72,7 +72,11 @@ def retrieve( return self._get( path_template("/models/{model}", model=model), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Model, ) @@ -95,7 +99,11 @@ def list( "/models", page=SyncPage[Model], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=Model, ) @@ -130,7 +138,11 @@ def delete( return self._delete( path_template("/models/{model}", model=model), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ModelDeleted, ) @@ -187,7 +199,11 @@ async def retrieve( return await self._get( path_template("/models/{model}", model=model), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Model, ) @@ -210,7 +226,11 @@ def list( "/models", page=AsyncPage[Model], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=Model, ) @@ -245,7 +265,11 @@ async def delete( return await self._delete( path_template("/models/{model}", model=model), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ModelDeleted, ) diff --git a/src/openai/resources/moderations.py b/src/openai/resources/moderations.py index 0b9a2d23c7..5ef4efeaa5 100644 --- a/src/openai/resources/moderations.py +++ b/src/openai/resources/moderations.py @@ -89,7 +89,11 @@ def create( moderation_create_params.ModerationCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ModerationCreateResponse, ) @@ -163,7 +167,11 @@ async def create( moderation_create_params.ModerationCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ModerationCreateResponse, ) diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index 8fa2569a96..7a4fcc0110 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -98,7 +98,11 @@ def create( call_create_params.CallCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -250,7 +254,11 @@ def accept( call_accept_params.CallAcceptParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -284,7 +292,11 @@ def hangup( return self._post( path_template("/realtime/calls/{call_id}/hangup", call_id=call_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -323,7 +335,11 @@ def refer( path_template("/realtime/calls/{call_id}/refer", call_id=call_id), body=maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -362,7 +378,11 @@ def reject( path_template("/realtime/calls/{call_id}/reject", call_id=call_id), body=maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -428,7 +448,11 @@ async def create( call_create_params.CallCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -580,7 +604,11 @@ async def accept( call_accept_params.CallAcceptParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -614,7 +642,11 @@ async def hangup( return await self._post( path_template("/realtime/calls/{call_id}/hangup", call_id=call_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -653,7 +685,11 @@ async def refer( path_template("/realtime/calls/{call_id}/refer", call_id=call_id), body=await async_maybe_transform({"target_uri": target_uri}, call_refer_params.CallReferParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -692,7 +728,11 @@ async def reject( path_template("/realtime/calls/{call_id}/reject", call_id=call_id), body=await async_maybe_transform({"status_code": status_code}, call_reject_params.CallRejectParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) diff --git a/src/openai/resources/realtime/client_secrets.py b/src/openai/resources/realtime/client_secrets.py index d9947dd7e8..7478e35e27 100644 --- a/src/openai/resources/realtime/client_secrets.py +++ b/src/openai/resources/realtime/client_secrets.py @@ -93,7 +93,11 @@ def create( client_secret_create_params.ClientSecretCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ClientSecretCreateResponse, ) @@ -175,7 +179,11 @@ async def create( client_secret_create_params.ClientSecretCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=ClientSecretCreateResponse, ) diff --git a/src/openai/resources/responses/input_items.py b/src/openai/resources/responses/input_items.py index b9ae5eeeae..1d3ed62543 100644 --- a/src/openai/resources/responses/input_items.py +++ b/src/openai/resources/responses/input_items.py @@ -101,6 +101,7 @@ def list( }, input_item_list_params.InputItemListParams, ), + security={"bearer_auth": True}, ), model=cast(Any, ResponseItem), # Union types cannot be passed in as arguments in the type system ) @@ -185,6 +186,7 @@ def list( }, input_item_list_params.InputItemListParams, ), + security={"bearer_auth": True}, ), model=cast(Any, ResponseItem), # Union types cannot be passed in as arguments in the type system ) diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py index 0056727fa0..fae71fd59c 100644 --- a/src/openai/resources/responses/input_tokens.py +++ b/src/openai/resources/responses/input_tokens.py @@ -143,7 +143,11 @@ def count( input_token_count_params.InputTokenCountParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=InputTokenCountResponse, ) @@ -269,7 +273,11 @@ async def count( input_token_count_params.InputTokenCountParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=InputTokenCountResponse, ) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 48705098cc..89a8ceffa1 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -953,7 +953,11 @@ def create( else response_create_params.ResponseCreateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Response, stream=stream or False, @@ -1505,6 +1509,7 @@ def retrieve( }, response_retrieve_params.ResponseRetrieveParams, ), + security={"bearer_auth": True}, ), cast_to=Response, stream=stream or False, @@ -1540,7 +1545,11 @@ def delete( return self._delete( path_template("/responses/{response_id}", response_id=response_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -1576,7 +1585,11 @@ def cancel( return self._post( path_template("/responses/{response_id}/cancel", response_id=response_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Response, ) @@ -1748,7 +1761,11 @@ def compact( response_compact_params.ResponseCompactParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=CompactedResponse, ) @@ -2634,7 +2651,11 @@ async def create( else response_create_params.ResponseCreateParamsNonStreaming, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Response, stream=stream or False, @@ -3190,6 +3211,7 @@ async def retrieve( }, response_retrieve_params.ResponseRetrieveParams, ), + security={"bearer_auth": True}, ), cast_to=Response, stream=stream or False, @@ -3225,7 +3247,11 @@ async def delete( return await self._delete( path_template("/responses/{response_id}", response_id=response_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=NoneType, ) @@ -3261,7 +3287,11 @@ async def cancel( return await self._post( path_template("/responses/{response_id}/cancel", response_id=response_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Response, ) @@ -3433,7 +3463,11 @@ async def compact( response_compact_params.ResponseCompactParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=CompactedResponse, ) diff --git a/src/openai/resources/skills/content.py b/src/openai/resources/skills/content.py index 96b237177e..d0639a5a52 100644 --- a/src/openai/resources/skills/content.py +++ b/src/openai/resources/skills/content.py @@ -69,7 +69,11 @@ def retrieve( return self._get( path_template("/skills/{skill_id}/content", skill_id=skill_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -124,7 +128,11 @@ async def retrieve( return await self._get( path_template("/skills/{skill_id}/content", skill_id=skill_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) diff --git a/src/openai/resources/skills/skills.py b/src/openai/resources/skills/skills.py index 764ed65e84..aeea43ca3e 100644 --- a/src/openai/resources/skills/skills.py +++ b/src/openai/resources/skills/skills.py @@ -114,7 +114,11 @@ def create( body=maybe_transform(body, skill_create_params.SkillCreateParams), files=extracted_files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Skill, ) @@ -147,7 +151,11 @@ def retrieve( return self._get( path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Skill, ) @@ -184,7 +192,11 @@ def update( path_template("/skills/{skill_id}", skill_id=skill_id), body=maybe_transform({"default_version": default_version}, skill_update_params.SkillUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Skill, ) @@ -237,6 +249,7 @@ def list( }, skill_list_params.SkillListParams, ), + security={"bearer_auth": True}, ), model=Skill, ) @@ -269,7 +282,11 @@ def delete( return self._delete( path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=DeletedSkill, ) @@ -340,7 +357,11 @@ async def create( body=await async_maybe_transform(body, skill_create_params.SkillCreateParams), files=extracted_files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Skill, ) @@ -373,7 +394,11 @@ async def retrieve( return await self._get( path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Skill, ) @@ -412,7 +437,11 @@ async def update( {"default_version": default_version}, skill_update_params.SkillUpdateParams ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Skill, ) @@ -465,6 +494,7 @@ def list( }, skill_list_params.SkillListParams, ), + security={"bearer_auth": True}, ), model=Skill, ) @@ -497,7 +527,11 @@ async def delete( return await self._delete( path_template("/skills/{skill_id}", skill_id=skill_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=DeletedSkill, ) diff --git a/src/openai/resources/skills/versions/content.py b/src/openai/resources/skills/versions/content.py index 2f54586718..173f94e4e1 100644 --- a/src/openai/resources/skills/versions/content.py +++ b/src/openai/resources/skills/versions/content.py @@ -74,7 +74,11 @@ def retrieve( return self._get( path_template("/skills/{skill_id}/versions/{version}/content", skill_id=skill_id, version=version), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -134,7 +138,11 @@ async def retrieve( return await self._get( path_template("/skills/{skill_id}/versions/{version}/content", skill_id=skill_id, version=version), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) diff --git a/src/openai/resources/skills/versions/versions.py b/src/openai/resources/skills/versions/versions.py index 2259306a86..d688d2917c 100644 --- a/src/openai/resources/skills/versions/versions.py +++ b/src/openai/resources/skills/versions/versions.py @@ -114,7 +114,11 @@ def create( body=maybe_transform(body, version_create_params.VersionCreateParams), files=extracted_files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=SkillVersion, ) @@ -152,7 +156,11 @@ def retrieve( return self._get( path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=SkillVersion, ) @@ -207,6 +215,7 @@ def list( }, version_list_params.VersionListParams, ), + security={"bearer_auth": True}, ), model=SkillVersion, ) @@ -244,7 +253,11 @@ def delete( return self._delete( path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=DeletedSkillVersion, ) @@ -323,7 +336,11 @@ async def create( body=await async_maybe_transform(body, version_create_params.VersionCreateParams), files=extracted_files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=SkillVersion, ) @@ -361,7 +378,11 @@ async def retrieve( return await self._get( path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=SkillVersion, ) @@ -416,6 +437,7 @@ def list( }, version_list_params.VersionListParams, ), + security={"bearer_auth": True}, ), model=SkillVersion, ) @@ -453,7 +475,11 @@ async def delete( return await self._delete( path_template("/skills/{skill_id}/versions/{version}", skill_id=skill_id, version=version), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=DeletedSkillVersion, ) diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py index 3891fc028c..a0c1e15223 100644 --- a/src/openai/resources/uploads/parts.py +++ b/src/openai/resources/uploads/parts.py @@ -91,7 +91,11 @@ def create( body=maybe_transform(body, part_create_params.PartCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=UploadPart, ) @@ -168,7 +172,11 @@ async def create( body=await async_maybe_transform(body, part_create_params.PartCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=UploadPart, ) diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py index 7778e51539..6c8c347f97 100644 --- a/src/openai/resources/uploads/uploads.py +++ b/src/openai/resources/uploads/uploads.py @@ -242,7 +242,11 @@ def create( upload_create_params.UploadCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Upload, ) @@ -278,7 +282,11 @@ def cancel( return self._post( path_template("/uploads/{upload_id}/cancel", upload_id=upload_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Upload, ) @@ -339,7 +347,11 @@ def complete( upload_complete_params.UploadCompleteParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Upload, ) @@ -558,7 +570,11 @@ async def create( upload_create_params.UploadCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Upload, ) @@ -594,7 +610,11 @@ async def cancel( return await self._post( path_template("/uploads/{upload_id}/cancel", upload_id=upload_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Upload, ) @@ -655,7 +675,11 @@ async def complete( upload_complete_params.UploadCompleteParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Upload, ) diff --git a/src/openai/resources/vector_stores/file_batches.py b/src/openai/resources/vector_stores/file_batches.py index 1ffd7642c0..4bde1a4aa6 100644 --- a/src/openai/resources/vector_stores/file_batches.py +++ b/src/openai/resources/vector_stores/file_batches.py @@ -113,7 +113,11 @@ def create( file_batch_create_params.FileBatchCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileBatch, ) @@ -154,7 +158,11 @@ def retrieve( batch_id=batch_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileBatch, ) @@ -197,7 +205,11 @@ def cancel( batch_id=batch_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileBatch, ) @@ -311,6 +323,7 @@ def list_files( }, file_batch_list_files_params.FileBatchListFilesParams, ), + security={"bearer_auth": True}, ), model=VectorStoreFile, ) @@ -488,7 +501,11 @@ async def create( file_batch_create_params.FileBatchCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileBatch, ) @@ -529,7 +546,11 @@ async def retrieve( batch_id=batch_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileBatch, ) @@ -572,7 +593,11 @@ async def cancel( batch_id=batch_id, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileBatch, ) @@ -686,6 +711,7 @@ def list_files( }, file_batch_list_files_params.FileBatchListFilesParams, ), + security={"bearer_auth": True}, ), model=VectorStoreFile, ) diff --git a/src/openai/resources/vector_stores/files.py b/src/openai/resources/vector_stores/files.py index 3ef6137267..b7e1ea9f92 100644 --- a/src/openai/resources/vector_stores/files.py +++ b/src/openai/resources/vector_stores/files.py @@ -102,7 +102,11 @@ def create( file_create_params.FileCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFile, ) @@ -141,7 +145,11 @@ def retrieve( "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFile, ) @@ -188,7 +196,11 @@ def update( ), body=maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFile, ) @@ -260,6 +272,7 @@ def list( }, file_list_params.FileListParams, ), + security={"bearer_auth": True}, ), model=VectorStoreFile, ) @@ -302,7 +315,11 @@ def delete( "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileDeleted, ) @@ -452,7 +469,11 @@ def content( ), page=SyncPage[FileContentResponse], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=FileContentResponse, ) @@ -535,7 +556,11 @@ async def create( file_create_params.FileCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFile, ) @@ -574,7 +599,11 @@ async def retrieve( "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFile, ) @@ -621,7 +650,11 @@ async def update( ), body=await async_maybe_transform({"attributes": attributes}, file_update_params.FileUpdateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFile, ) @@ -693,6 +726,7 @@ def list( }, file_list_params.FileListParams, ), + security={"bearer_auth": True}, ), model=VectorStoreFile, ) @@ -735,7 +769,11 @@ async def delete( "/vector_stores/{vector_store_id}/files/{file_id}", vector_store_id=vector_store_id, file_id=file_id ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreFileDeleted, ) @@ -887,7 +925,11 @@ def content( ), page=AsyncPage[FileContentResponse], options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=FileContentResponse, ) diff --git a/src/openai/resources/vector_stores/vector_stores.py b/src/openai/resources/vector_stores/vector_stores.py index 7fa2ad5274..e4c5d1440c 100644 --- a/src/openai/resources/vector_stores/vector_stores.py +++ b/src/openai/resources/vector_stores/vector_stores.py @@ -139,7 +139,11 @@ def create( vector_store_create_params.VectorStoreCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStore, ) @@ -173,7 +177,11 @@ def retrieve( return self._get( path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStore, ) @@ -229,7 +237,11 @@ def update( vector_store_update_params.VectorStoreUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStore, ) @@ -295,6 +307,7 @@ def list( }, vector_store_list_params.VectorStoreListParams, ), + security={"bearer_auth": True}, ), model=VectorStore, ) @@ -328,7 +341,11 @@ def delete( return self._delete( path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreDeleted, ) @@ -390,7 +407,11 @@ def search( vector_store_search_params.VectorStoreSearchParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=VectorStoreSearchResponse, method="post", @@ -489,7 +510,11 @@ async def create( vector_store_create_params.VectorStoreCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStore, ) @@ -523,7 +548,11 @@ async def retrieve( return await self._get( path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStore, ) @@ -579,7 +608,11 @@ async def update( vector_store_update_params.VectorStoreUpdateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStore, ) @@ -645,6 +678,7 @@ def list( }, vector_store_list_params.VectorStoreListParams, ), + security={"bearer_auth": True}, ), model=VectorStore, ) @@ -678,7 +712,11 @@ async def delete( return await self._delete( path_template("/vector_stores/{vector_store_id}", vector_store_id=vector_store_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VectorStoreDeleted, ) @@ -740,7 +778,11 @@ def search( vector_store_search_params.VectorStoreSearchParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), model=VectorStoreSearchResponse, method="post", diff --git a/src/openai/resources/videos.py b/src/openai/resources/videos.py index 3b80400cac..e9bad80afe 100644 --- a/src/openai/resources/videos.py +++ b/src/openai/resources/videos.py @@ -125,7 +125,11 @@ def create( body=maybe_transform(body, video_create_params.VideoCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -231,7 +235,11 @@ def retrieve( return self._get( path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -284,6 +292,7 @@ def list( }, video_list_params.VideoListParams, ), + security={"bearer_auth": True}, ), model=Video, ) @@ -316,7 +325,11 @@ def delete( return self._delete( path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VideoDeleteResponse, ) @@ -366,7 +379,11 @@ def create_character( body=maybe_transform(body, video_create_character_params.VideoCreateCharacterParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VideoCreateCharacterResponse, ) @@ -410,6 +427,7 @@ def download_content( extra_body=extra_body, timeout=timeout, query=maybe_transform({"variant": variant}, video_download_content_params.VideoDownloadContentParams), + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -460,7 +478,11 @@ def edit( body=maybe_transform(body, video_edit_params.VideoEditParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -515,7 +537,11 @@ def extend( body=maybe_transform(body, video_extend_params.VideoExtendParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -548,7 +574,11 @@ def get_character( return self._get( path_template("/videos/characters/{character_id}", character_id=character_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VideoGetCharacterResponse, ) @@ -585,7 +615,11 @@ def remix( path_template("/videos/{video_id}/remix", video_id=video_id), body=maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -670,7 +704,11 @@ async def create( body=await async_maybe_transform(body, video_create_params.VideoCreateParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -776,7 +814,11 @@ async def retrieve( return await self._get( path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -829,6 +871,7 @@ def list( }, video_list_params.VideoListParams, ), + security={"bearer_auth": True}, ), model=Video, ) @@ -861,7 +904,11 @@ async def delete( return await self._delete( path_template("/videos/{video_id}", video_id=video_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VideoDeleteResponse, ) @@ -911,7 +958,11 @@ async def create_character( body=await async_maybe_transform(body, video_create_character_params.VideoCreateCharacterParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VideoCreateCharacterResponse, ) @@ -957,6 +1008,7 @@ async def download_content( query=await async_maybe_transform( {"variant": variant}, video_download_content_params.VideoDownloadContentParams ), + security={"bearer_auth": True}, ), cast_to=_legacy_response.HttpxBinaryResponseContent, ) @@ -1007,7 +1059,11 @@ async def edit( body=await async_maybe_transform(body, video_edit_params.VideoEditParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -1062,7 +1118,11 @@ async def extend( body=await async_maybe_transform(body, video_extend_params.VideoExtendParams), files=files, options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) @@ -1095,7 +1155,11 @@ async def get_character( return await self._get( path_template("/videos/characters/{character_id}", character_id=character_id), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=VideoGetCharacterResponse, ) @@ -1132,7 +1196,11 @@ async def remix( path_template("/videos/{video_id}/remix", video_id=video_id), body=await async_maybe_transform({"prompt": prompt}, video_remix_params.VideoRemixParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Video, ) diff --git a/src/openai/types/admin/__init__.py b/src/openai/types/admin/__init__.py new file mode 100644 index 0000000000..f8ee8b14b1 --- /dev/null +++ b/src/openai/types/admin/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/tests/conftest.py b/tests/conftest.py index 408bcf76c0..1042fe59d9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -46,6 +46,7 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" +admin_api_key = "My Admin API Key" @pytest.fixture(scope="session") @@ -54,7 +55,9 @@ def client(request: FixtureRequest) -> Iterator[OpenAI]: if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - with OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + with OpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=strict + ) as client: yield client @@ -79,6 +82,10 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenAI]: raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") async with AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=strict, + http_client=http_client, ) as client: yield client diff --git a/tests/test_client.py b/tests/test_client.py index 570042c46a..369d746b2d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -25,7 +25,7 @@ from openai._utils import asyncify from openai._models import BaseModel, FinalRequestOptions from openai._streaming import Stream, AsyncStream -from openai._exceptions import OpenAIError, APIStatusError, APITimeoutError, APIResponseValidationError +from openai._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError from openai._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, @@ -42,19 +42,7 @@ T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" -workload_identity: WorkloadIdentity = { - "client_id": "client-id", - "identity_provider_id": "identity-provider-id", - "service_account_id": "service-account-id", - "provider": { - "token_type": "jwt", - "get_token": lambda: "external-subject-token", - }, -} - - -class MockRequestCall(Protocol): - request: httpx.Request +admin_api_key = "My Admin API Key" def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: @@ -155,6 +143,10 @@ def test_copy(self, client: OpenAI) -> None: assert copied.api_key == "another My API Key" assert client.api_key == "My API Key" + copied = client.copy(admin_api_key="another My Admin API Key") + assert copied.admin_api_key == "another My Admin API Key" + assert client.admin_api_key == "My Admin API Key" + def test_copy_default_options(self, client: OpenAI) -> None: # options that have a default are overridden correctly copied = client.copy(max_retries=7) @@ -173,7 +165,11 @@ def test_copy_default_options(self, client: OpenAI) -> None: def test_copy_default_headers(self) -> None: client = OpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) assert client.default_headers["X-Foo"] == "bar" @@ -208,7 +204,11 @@ def test_copy_default_headers(self) -> None: def test_copy_default_query(self) -> None: client = OpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_query={"foo": "bar"}, ) assert _get_params(client)["foo"] == "bar" @@ -333,7 +333,13 @@ def test_request_timeout(self, client: OpenAI) -> None: assert timeout == httpx.Timeout(100.0) def test_client_timeout_option(self) -> None: - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0)) + client = OpenAI( + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + timeout=httpx.Timeout(0), + ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -345,7 +351,11 @@ def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: client = OpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -357,7 +367,11 @@ def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = OpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -369,7 +383,11 @@ def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = OpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -384,13 +402,18 @@ async def test_invalid_http_client(self) -> None: OpenAI( base_url=base_url, api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=cast(Any, http_client), ) def test_default_headers_option(self) -> None: test_client = OpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" @@ -399,6 +422,7 @@ def test_default_headers_option(self) -> None: test_client2 = OpenAI( base_url=base_url, api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -413,16 +437,72 @@ def test_default_headers_option(self) -> None: test_client2.close() def test_validate_headers(self) -> None: - client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + test_client.close() + test_client2.close() + + def test_validate_headers(self) -> None: + client = OpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) options = client._prepare_options(FinalRequestOptions(method="get", url="/foo")) request = client._build_request(options) assert request.headers.get("Authorization") == f"Bearer {api_key}" - with pytest.raises(OpenAIError): - with update_env(**{"OPENAI_API_KEY": Omit()}): - client2 = OpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) - _ = client2 + admin_request = client._build_request( + FinalRequestOptions( + method="get", + url="/organization/projects", + security={"admin_api_key_auth": True}, + ) + ) + assert admin_request.headers.get("Authorization") == f"Bearer {admin_api_key}" + + with update_env(**{"OPENAI_API_KEY": Omit()}): + admin_only = OpenAI( + base_url=base_url, + api_key=None, + admin_api_key=admin_api_key, + _strict_response_validation=True, + ) + admin_only_request = admin_only._build_request( + FinalRequestOptions( + method="get", + url="/organization/projects", + security={"admin_api_key_auth": True}, + ) + ) + assert admin_only_request.headers.get("Authorization") == f"Bearer {admin_api_key}" + + with pytest.raises( + TypeError, + match="Could not resolve authentication method", + ): + admin_only._build_request( + FinalRequestOptions( + method="post", + url="/responses", + security={"bearer_auth": True}, + ) + ) + + with update_env( + **{ + "OPENAI_API_KEY": Omit(), + "OPENAI_ADMIN_KEY": Omit(), + } + ): + with pytest.raises(OpenAIError, match="Missing credentials"): + OpenAI(base_url=base_url, api_key=None, admin_api_key=None, _strict_response_validation=True) def test_workload_identity_is_mutually_exclusive_with_api_key(self) -> None: with pytest.raises( @@ -439,7 +519,11 @@ def test_workload_identity_is_mutually_exclusive_with_api_key(self) -> None: def test_default_query_option(self) -> None: client = OpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_query={"query_param": "bar"}, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -636,6 +720,7 @@ def mock_handler(request: httpx.Request) -> httpx.Response: with OpenAI( base_url=base_url, api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), ) as client: @@ -729,7 +814,12 @@ class Model(BaseModel): assert response.foo == 2 def test_base_url_setter(self) -> None: - client = OpenAI(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) + client = OpenAI( + base_url="https://example.com/from_init", + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + ) assert client.base_url == "https://example.com/from_init/" client.base_url = "https://example.com/from_setter" # type: ignore[assignment] @@ -740,16 +830,22 @@ def test_base_url_setter(self) -> None: def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): - client = OpenAI(api_key=api_key, _strict_response_validation=True) + client = OpenAI(api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @pytest.mark.parametrize( "client", [ - OpenAI(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), OpenAI( base_url="http://localhost:5000/custom/path/", api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + ), + OpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -770,10 +866,16 @@ def test_base_url_trailing_slash(self, client: OpenAI) -> None: @pytest.mark.parametrize( "client", [ - OpenAI(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), OpenAI( base_url="http://localhost:5000/custom/path/", api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + ), + OpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -794,10 +896,16 @@ def test_base_url_no_trailing_slash(self, client: OpenAI) -> None: @pytest.mark.parametrize( "client", [ - OpenAI(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), OpenAI( base_url="http://localhost:5000/custom/path/", api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + ), + OpenAI( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -816,7 +924,9 @@ def test_absolute_request_url(self, client: OpenAI) -> None: client.close() def test_copied_client_does_not_close_http(self) -> None: - test_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = OpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) assert not test_client.is_closed() copied = test_client.copy() @@ -827,7 +937,9 @@ def test_copied_client_does_not_close_http(self) -> None: assert not test_client.is_closed() def test_client_context_manager(self) -> None: - test_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = OpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) with test_client as c2: assert c2 is test_client assert not c2.is_closed() @@ -848,7 +960,13 @@ class Model(BaseModel): def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): - OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) + OpenAI( + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + max_retries=cast(Any, None), + ) @pytest.mark.respx(base_url=base_url) def test_default_stream_cls(self, respx_mock: MockRouter, client: OpenAI) -> None: @@ -868,12 +986,16 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + strict_client = OpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - non_strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = OpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=False + ) response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] @@ -1221,6 +1343,10 @@ def test_copy(self, async_client: AsyncOpenAI) -> None: assert copied.api_key == "another My API Key" assert async_client.api_key == "My API Key" + copied = async_client.copy(admin_api_key="another My Admin API Key") + assert copied.admin_api_key == "another My Admin API Key" + assert async_client.admin_api_key == "My Admin API Key" + def test_copy_default_options(self, async_client: AsyncOpenAI) -> None: # options that have a default are overridden correctly copied = async_client.copy(max_retries=7) @@ -1239,7 +1365,11 @@ def test_copy_default_options(self, async_client: AsyncOpenAI) -> None: async def test_copy_default_headers(self) -> None: client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) assert client.default_headers["X-Foo"] == "bar" @@ -1274,7 +1404,11 @@ async def test_copy_default_headers(self) -> None: async def test_copy_default_query(self) -> None: client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_query={"foo": "bar"}, ) assert _get_params(client)["foo"] == "bar" @@ -1402,7 +1536,11 @@ async def test_request_timeout(self, async_client: AsyncOpenAI) -> None: async def test_client_timeout_option(self) -> None: client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + timeout=httpx.Timeout(0), ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1415,7 +1553,11 @@ async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1427,7 +1569,11 @@ async def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1439,7 +1585,11 @@ async def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + http_client=http_client, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1454,13 +1604,18 @@ def test_invalid_http_client(self) -> None: AsyncOpenAI( base_url=base_url, api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=cast(Any, http_client), ) async def test_default_headers_option(self) -> None: test_client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" @@ -1469,6 +1624,7 @@ async def test_default_headers_option(self) -> None: test_client2 = AsyncOpenAI( base_url=base_url, api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -1483,19 +1639,66 @@ async def test_default_headers_option(self) -> None: await test_client2.close() async def test_validate_headers(self) -> None: - client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + client = AsyncOpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) options = await client._prepare_options(FinalRequestOptions(method="get", url="/foo")) request = client._build_request(options) assert request.headers.get("Authorization") == f"Bearer {api_key}" - with pytest.raises(OpenAIError): - with update_env(**{"OPENAI_API_KEY": Omit()}): - client2 = AsyncOpenAI(base_url=base_url, api_key=None, _strict_response_validation=True) - _ = client2 + admin_request = client._build_request( + FinalRequestOptions( + method="get", + url="/organization/projects", + security={"admin_api_key_auth": True}, + ) + ) + assert admin_request.headers.get("Authorization") == f"Bearer {admin_api_key}" + + with update_env(**{"OPENAI_API_KEY": Omit()}): + admin_only = AsyncOpenAI( + base_url=base_url, + api_key=None, + admin_api_key=admin_api_key, + _strict_response_validation=True, + ) + admin_only_request = admin_only._build_request( + FinalRequestOptions( + method="get", + url="/organization/projects", + security={"admin_api_key_auth": True}, + ) + ) + assert admin_only_request.headers.get("Authorization") == f"Bearer {admin_api_key}" + + with pytest.raises( + TypeError, + match="Could not resolve authentication method", + ): + admin_only._build_request( + FinalRequestOptions( + method="post", + url="/responses", + security={"bearer_auth": True}, + ) + ) + + with update_env( + **{ + "OPENAI_API_KEY": Omit(), + "OPENAI_ADMIN_KEY": Omit(), + } + ): + with pytest.raises(OpenAIError, match="Missing credentials"): + AsyncOpenAI(base_url=base_url, api_key=None, admin_api_key=None, _strict_response_validation=True) async def test_default_query_option(self) -> None: client = AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + default_query={"query_param": "bar"}, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -1692,6 +1895,7 @@ async def mock_handler(request: httpx.Request) -> httpx.Response: async with AsyncOpenAI( base_url=base_url, api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), ) as client: @@ -1790,7 +1994,10 @@ class Model(BaseModel): async def test_base_url_setter(self) -> None: client = AsyncOpenAI( - base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True + base_url="https://example.com/from_init", + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, ) assert client.base_url == "https://example.com/from_init/" @@ -1802,18 +2009,22 @@ async def test_base_url_setter(self) -> None: async def test_base_url_env(self) -> None: with update_env(OPENAI_BASE_URL="http://localhost:5000/from/env"): - client = AsyncOpenAI(api_key=api_key, _strict_response_validation=True) + client = AsyncOpenAI(api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @pytest.mark.parametrize( "client", [ AsyncOpenAI( - base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, ), AsyncOpenAI( base_url="http://localhost:5000/custom/path/", api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1835,11 +2046,15 @@ async def test_base_url_trailing_slash(self, client: AsyncOpenAI) -> None: "client", [ AsyncOpenAI( - base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, ), AsyncOpenAI( base_url="http://localhost:5000/custom/path/", api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1861,11 +2076,15 @@ async def test_base_url_no_trailing_slash(self, client: AsyncOpenAI) -> None: "client", [ AsyncOpenAI( - base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, ), AsyncOpenAI( base_url="http://localhost:5000/custom/path/", api_key=api_key, + admin_api_key=admin_api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1884,7 +2103,9 @@ async def test_absolute_request_url(self, client: AsyncOpenAI) -> None: await client.close() async def test_copied_client_does_not_close_http(self) -> None: - test_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = AsyncOpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) assert not test_client.is_closed() copied = test_client.copy() @@ -1896,7 +2117,9 @@ async def test_copied_client_does_not_close_http(self) -> None: assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - test_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = AsyncOpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) async with test_client as c2: assert c2 is test_client assert not c2.is_closed() @@ -1918,7 +2141,11 @@ class Model(BaseModel): async def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): AsyncOpenAI( - base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=True, + max_retries=cast(Any, None), ) @pytest.mark.respx(base_url=base_url) @@ -1939,12 +2166,16 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True) + strict_client = AsyncOpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True + ) with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - non_strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = AsyncOpenAI( + base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=False + ) response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 9c9a1addab..6371ae7057 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -14,7 +14,8 @@ def reset_state() -> None: openai._reset_client() - openai.api_key = None or "My API Key" + openai.api_key = None + openai.admin_api_key = None openai.organization = None openai.project = None openai.webhook_secret = None From 20f81f8c64862cf222d625cbc2a15e372939fbea Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Wed, 29 Apr 2026 10:09:07 -0400 Subject: [PATCH 315/408] fix(api): support admin api key auth --- src/openai/_client.py | 170 ++++++++++++++--------------- src/openai/resources/embeddings.py | 1 - tests/test_client.py | 26 ++--- 3 files changed, 92 insertions(+), 105 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index 3009989beb..8a81a057a3 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -29,10 +29,9 @@ get_async_library, ) from ._compat import cached_property - from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream -from ._exceptions import APIStatusError +from ._exceptions import OpenAIError, APIStatusError from ._base_client import ( DEFAULT_MAX_RETRIES, SyncAPIClient, @@ -89,8 +88,6 @@ WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER = "workload-identity-auth" -class OpenAI(SyncAPIClient): - # client options class OpenAI(SyncAPIClient): # client options api_key: str | None @@ -137,6 +134,7 @@ def __init__( # outlining your use-case to help us decide if it should be # part of our public interface in the future. _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: """Construct a new synchronous OpenAI client instance. @@ -173,6 +171,17 @@ def __init__( admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") self.admin_api_key = admin_api_key + if ( + _enforce_credentials + and not self.api_key + and self._api_key_provider is None + and workload_identity is None + and self.admin_api_key is None + ): + raise OpenAIError( + "Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable." + ) + if organization is None: organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization @@ -371,51 +380,25 @@ def with_streaming_response(self) -> OpenAIWithStreamedResponse: def qs(self) -> Querystring: return Querystring(array_format="brackets") - - @override - def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: - return { - **(self._bearer_auth if security.get("bearer_auth", False) else {}), - **(self._admin_api_key_auth if security.get("admin_api_key_auth", False) else {}), - } - - @property - def _bearer_auth(self) -> dict[str, str]: - api_key = self.api_key - - return {"Authorization": f"Bearer {api_key}"} - - @property - def _admin_api_key_auth(self) -> dict[str, str]: - admin_api_key = self.admin_api_key - if admin_api_key is None: - return {} - return {"Authorization": f"Bearer {admin_api_key}"} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-Stainless-Async": "false", - "OpenAI-Organization": self.organization if self.organization is not None else Omit(), - "OpenAI-Project": self.project if self.project is not None else Omit(), - **self._custom_headers, - } - - @override - def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): - return - - raise TypeError( - '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' - ) - - def copy( + def _send_with_auth_retry( self, + request: httpx.Request, *, + stream: bool, + retried: bool = False, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + if self._api_key_provider is not None: + request.headers["Authorization"] = f"Bearer {self._refresh_api_key()}" + + if self._workload_identity_auth is not None: + request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" + response = super()._send_request(request, stream=stream, **kwargs) + if response.status_code == 401 and self._workload_identity_auth is not None and not retried: + response.close() + self._workload_identity_auth.invalidate_token() + request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" return self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) return response @@ -480,10 +463,19 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): return + if self._api_key_provider is not None or self._workload_identity_auth is not None: + return + raise TypeError( '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) + def _refresh_api_key(self) -> str | None: + if self._api_key_provider is not None: + self.api_key = self._api_key_provider() + + return self.api_key + def copy( self, *, @@ -502,6 +494,7 @@ def copy( set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ @@ -528,6 +521,7 @@ def copy( http_client = http_client or self._client return self.__class__( + api_key=api_key or self._api_key_provider or self.api_key, admin_api_key=admin_api_key or self.admin_api_key, workload_identity=workload_identity or self.workload_identity, organization=organization or self.organization, @@ -540,6 +534,7 @@ def copy( max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, + _enforce_credentials=_enforce_credentials, **_extra_kwargs, ) @@ -586,6 +581,7 @@ class AsyncOpenAI(AsyncAPIClient): # client options api_key: str | None admin_api_key: str | None + workload_identity: WorkloadIdentity | None organization: str | None project: str | None webhook_secret: str | None @@ -602,7 +598,6 @@ class AsyncOpenAI(AsyncAPIClient): def __init__( self, *, - *, api_key: str | Callable[[], Awaitable[str]] | None = None, admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, @@ -628,6 +623,7 @@ def __init__( # outlining your use-case to help us decide if it should be # part of our public interface in the future. _strict_response_validation: bool = False, + _enforce_credentials: bool = True, ) -> None: """Construct a new async AsyncOpenAI client instance. @@ -664,6 +660,17 @@ def __init__( admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") self.admin_api_key = admin_api_key + if ( + _enforce_credentials + and not self.api_key + and self._api_key_provider is None + and workload_identity is None + and self.admin_api_key is None + ): + raise OpenAIError( + "Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable." + ) + if organization is None: organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization @@ -862,51 +869,25 @@ def with_streaming_response(self) -> AsyncOpenAIWithStreamedResponse: def qs(self) -> Querystring: return Querystring(array_format="brackets") - - @override - def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: - return { - **(self._bearer_auth if security.get("bearer_auth", False) else {}), - **(self._admin_api_key_auth if security.get("admin_api_key_auth", False) else {}), - } - - @property - def _bearer_auth(self) -> dict[str, str]: - api_key = self.api_key - - return {"Authorization": f"Bearer {api_key}"} - - @property - def _admin_api_key_auth(self) -> dict[str, str]: - admin_api_key = self.admin_api_key - if admin_api_key is None: - return {} - return {"Authorization": f"Bearer {admin_api_key}"} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-Stainless-Async": f"async:{get_async_library()}", - "OpenAI-Organization": self.organization if self.organization is not None else Omit(), - "OpenAI-Project": self.project if self.project is not None else Omit(), - **self._custom_headers, - } - - @override - def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): - return - - raise TypeError( - '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' - ) - - def copy( + async def _send_with_auth_retry( self, + request: httpx.Request, *, + stream: bool, + retried: bool = False, + **kwargs: Unpack[HttpxSendArgs], + ) -> httpx.Response: + if self._api_key_provider is not None: + request.headers["Authorization"] = f"Bearer {await self._refresh_api_key()}" + + if self._workload_identity_auth is not None: + request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" + response = await super()._send_request(request, stream=stream, **kwargs) + if response.status_code == 401 and self._workload_identity_auth is not None and not retried: + await response.aclose() + self._workload_identity_auth.invalidate_token() + request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" return await self._send_with_auth_retry(request, stream=stream, retried=True, **kwargs) return response @@ -971,10 +952,19 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): return + if self._api_key_provider is not None or self._workload_identity_auth is not None: + return + raise TypeError( '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) + async def _refresh_api_key(self) -> str | None: + if self._api_key_provider is not None: + self.api_key = await self._api_key_provider() + + return self.api_key + def copy( self, *, @@ -993,6 +983,7 @@ def copy( set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ @@ -1031,6 +1022,7 @@ def copy( max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, + _enforce_credentials=_enforce_credentials, **_extra_kwargs, ) diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index 6f44ebac96..fea6255c0d 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -259,7 +259,6 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: return await self._post( "/embeddings", body=maybe_transform(params, embedding_create_params.EmbeddingCreateParams), - options=make_request_options( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/tests/test_client.py b/tests/test_client.py index 369d746b2d..f969bdea36 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,7 +10,7 @@ import inspect import dataclasses import tracemalloc -from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Protocol, Coroutine, cast +from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast from unittest import mock from typing_extensions import Literal, AsyncIterator, override @@ -19,7 +19,7 @@ from respx import MockRouter from pydantic import ValidationError -from openai import OpenAI, AsyncOpenAI, APIResponseValidationError +from openai import OpenAI, AsyncOpenAI, OpenAIError, APIResponseValidationError from openai.auth import WorkloadIdentity from openai._types import Omit from openai._utils import asyncify @@ -43,6 +43,15 @@ base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" admin_api_key = "My Admin API Key" +workload_identity: WorkloadIdentity = { + "client_id": "client_123", + "identity_provider_id": "provider_123", + "service_account_id": "service_account_123", + "provider": { + "get_token": lambda: "external-subject-token", + "token_type": "jwt", + }, +} def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: @@ -436,19 +445,6 @@ def test_default_headers_option(self) -> None: test_client.close() test_client2.close() - def test_validate_headers(self) -> None: - default_headers={ - "X-Foo": "stainless", - "X-Stainless-Lang": "my-overriding-header", - }, - ) - request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) - assert request.headers.get("x-foo") == "stainless" - assert request.headers.get("x-stainless-lang") == "my-overriding-header" - - test_client.close() - test_client2.close() - def test_validate_headers(self) -> None: client = OpenAI( base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=True From 6c51347b20d4fc95ad357056fa0384ed77a68656 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Wed, 29 Apr 2026 11:28:12 -0400 Subject: [PATCH 316/408] fix(api): resolve python auth type checks --- src/openai/_client.py | 5 +++-- tests/test_client.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index 8a81a057a3..45a4f12945 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -29,6 +29,7 @@ get_async_library, ) from ._compat import cached_property +from ._models import SecurityOptions from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError @@ -534,7 +535,7 @@ def copy( max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, - _enforce_credentials=_enforce_credentials, + _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, **_extra_kwargs, ) @@ -1022,7 +1023,7 @@ def copy( max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, - _enforce_credentials=_enforce_credentials, + _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, **_extra_kwargs, ) diff --git a/tests/test_client.py b/tests/test_client.py index f969bdea36..3f3288158f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -18,6 +18,7 @@ import pytest from respx import MockRouter from pydantic import ValidationError +from respx.models import Call as MockRequestCall from openai import OpenAI, AsyncOpenAI, OpenAIError, APIResponseValidationError from openai.auth import WorkloadIdentity From 3c1a41a2ee235e73703800917ed3027964f94c12 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Wed, 29 Apr 2026 11:36:52 -0400 Subject: [PATCH 317/408] fix(api): preserve python api key attribute type --- src/openai/_client.py | 12 ++++++------ src/openai/lib/azure.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index 45a4f12945..2937cd89f2 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -91,7 +91,7 @@ class OpenAI(SyncAPIClient): # client options - api_key: str | None + api_key: str admin_api_key: str | None workload_identity: WorkloadIdentity | None organization: str | None @@ -164,7 +164,7 @@ def __init__( self.api_key = "" self._api_key_provider: Callable[[], str] | None = api_key # type: ignore[no-redef] else: - self.api_key = api_key + self.api_key = api_key or "" self._api_key_provider = None self._workload_identity_auth = None @@ -471,7 +471,7 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) - def _refresh_api_key(self) -> str | None: + def _refresh_api_key(self) -> str: if self._api_key_provider is not None: self.api_key = self._api_key_provider() @@ -580,7 +580,7 @@ def _make_status_error( class AsyncOpenAI(AsyncAPIClient): # client options - api_key: str | None + api_key: str admin_api_key: str | None workload_identity: WorkloadIdentity | None organization: str | None @@ -653,7 +653,7 @@ def __init__( self.api_key = "" self._api_key_provider: Callable[[], Awaitable[str]] | None = api_key # type: ignore[no-redef] else: - self.api_key = api_key + self.api_key = api_key or "" self._api_key_provider = None self._workload_identity_auth = None @@ -960,7 +960,7 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) - async def _refresh_api_key(self) -> str | None: + async def _refresh_api_key(self) -> str: if self._api_key_provider is not None: self.api_key = await self._api_key_provider() diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index a7b23f8de9..a43316df98 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -348,7 +348,7 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: if azure_ad_token is not None: if headers.get("Authorization") is None: headers["Authorization"] = f"Bearer {azure_ad_token}" - elif self.api_key is not None and self.api_key is not API_KEY_SENTINEL: + elif self.api_key and self.api_key != API_KEY_SENTINEL: if headers.get("api-key") is None: headers["api-key"] = self.api_key else: @@ -649,7 +649,7 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp if azure_ad_token is not None: if headers.get("Authorization") is None: headers["Authorization"] = f"Bearer {azure_ad_token}" - elif self.api_key is not None and self.api_key is not API_KEY_SENTINEL: + elif self.api_key and self.api_key != API_KEY_SENTINEL: if headers.get("api-key") is None: headers["api-key"] = self.api_key else: From daaf92f3fdc127defbb90c9401a785c097d62890 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:53:44 +0000 Subject: [PATCH 318/408] ci: pin trigger-release-please github action --- .github/workflows/create-releases.yml | 2 +- .stats.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index f819d07310..f39b4c3e2c 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: stainless-api/trigger-release-please@bb6677c5a04578eec1ccfd9e1913b5b78ed64c61 # v1 + - uses: stainless-api/trigger-release-please@bb6677c5a04578eec1ccfd9e1913b5b78ed64c61 # v1.4.0 id: release with: repo: ${{ github.event.repository.full_name }} diff --git a/.stats.yml b/.stats.yml index 613a7fb245..58e688f32e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fa32d6e7d961e6a9090bfd42dada0302b1b11c40308587ea36bd46be854f33ab.yml openapi_spec_hash: e582137699583c1fb37ddb0553a8a2d0 -config_hash: 5dcd82ad6d930cf0a04c3cd7bbda82cc +config_hash: 2b2791e02f87210a840b88c95ccefd31 From efd9803e5a3e412e9f3429d94dce2178d797d3db Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:31:53 +0000 Subject: [PATCH 319/408] fix(types): correct timestamp types to int in Response model --- .stats.yml | 4 ++-- src/openai/types/responses/response.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 58e688f32e..7c7f8e0870 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-fa32d6e7d961e6a9090bfd42dada0302b1b11c40308587ea36bd46be854f33ab.yml -openapi_spec_hash: e582137699583c1fb37ddb0553a8a2d0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5b98524f8deee980c0dc96ceef2dd29d3e46c0ef56a3e68c6fef6e0ea6452bd0.yml +openapi_spec_hash: 2ce630b5996b5c36aaf8be25d0c41183 config_hash: 2b2791e02f87210a840b88c95ccefd31 diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 0d2491ea7c..bfcf2460d6 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -60,7 +60,7 @@ class Response(BaseModel): id: str """Unique identifier for this Response.""" - created_at: float + created_at: int """Unix timestamp (in seconds) of when this Response was created.""" error: Optional[ResponseError] = None @@ -165,7 +165,7 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/background). """ - completed_at: Optional[float] = None + completed_at: Optional[int] = None """ Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`. From 4dcd06747b37d0a87004e729804bc6fbbbc104c6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:14:37 +0000 Subject: [PATCH 320/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 7c7f8e0870..f7293f7c7e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5b98524f8deee980c0dc96ceef2dd29d3e46c0ef56a3e68c6fef6e0ea6452bd0.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5b98524f8deee980c0dc96ceef2dd29d3e46c0ef56a3e68c6fef6e0ea6452bd0.yml openapi_spec_hash: 2ce630b5996b5c36aaf8be25d0c41183 config_hash: 2b2791e02f87210a840b88c95ccefd31 From 9cefa5c6be6d5e7fff19c63244baf4a82ab2c285 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:24:43 +0000 Subject: [PATCH 321/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f7293f7c7e..6f1a2c220a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 152 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5b98524f8deee980c0dc96ceef2dd29d3e46c0ef56a3e68c6fef6e0ea6452bd0.yml -openapi_spec_hash: 2ce630b5996b5c36aaf8be25d0c41183 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-617ac7ff7388ed68d19955b74d6dd1a3e2313e5bc47eb7c7a138162e29e23b71.yml +openapi_spec_hash: d431cf9d2bf2b01122bb98cc7b7a357b config_hash: 2b2791e02f87210a840b88c95ccefd31 From af5934cfcc5140006aa777dc46297c0a7fa89b9a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 23:41:43 +0000 Subject: [PATCH 322/408] feat(api): manual updates Generate SDK for admin.organization.audit_logs. --- .stats.yml | 4 +- api.md | 16 + src/openai/__init__.py | 1 + src/openai/_client.py | 38 + src/openai/_module_client.py | 8 + src/openai/resources/__init__.py | 14 + src/openai/resources/admin/__init__.py | 33 + src/openai/resources/admin/admin.py | 102 ++ .../resources/admin/organization/__init__.py | 33 + .../admin/organization/audit_logs.py | 387 ++++++ .../admin/organization/organization.py | 108 ++ .../types/admin/organization/__init__.py | 6 + .../organization/audit_log_list_params.py | 143 +++ .../organization/audit_log_list_response.py | 1033 +++++++++++++++++ tests/api_resources/admin/__init__.py | 1 + .../admin/organization/__init__.py | 1 + .../admin/organization/test_audit_logs.py | 115 ++ 17 files changed, 2041 insertions(+), 2 deletions(-) create mode 100644 src/openai/resources/admin/__init__.py create mode 100644 src/openai/resources/admin/admin.py create mode 100644 src/openai/resources/admin/organization/__init__.py create mode 100644 src/openai/resources/admin/organization/audit_logs.py create mode 100644 src/openai/resources/admin/organization/organization.py create mode 100644 src/openai/types/admin/organization/__init__.py create mode 100644 src/openai/types/admin/organization/audit_log_list_params.py create mode 100644 src/openai/types/admin/organization/audit_log_list_response.py create mode 100644 tests/api_resources/admin/__init__.py create mode 100644 tests/api_resources/admin/organization/__init__.py create mode 100644 tests/api_resources/admin/organization/test_audit_logs.py diff --git a/.stats.yml b/.stats.yml index 6f1a2c220a..838ad705ab 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 152 +configured_endpoints: 153 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-617ac7ff7388ed68d19955b74d6dd1a3e2313e5bc47eb7c7a138162e29e23b71.yml openapi_spec_hash: d431cf9d2bf2b01122bb98cc7b7a357b -config_hash: 2b2791e02f87210a840b88c95ccefd31 +config_hash: 88be7d18ee7f33affcdad19572cd0c91 diff --git a/api.md b/api.md index decf4e0129..c1378fa60f 100644 --- a/api.md +++ b/api.md @@ -707,6 +707,22 @@ Methods: - client.uploads.parts.create(upload_id, \*\*params) -> UploadPart +# Admin + +## Organization + +### AuditLogs + +Types: + +```python +from openai.types.admin.organization import AuditLogListResponse +``` + +Methods: + +- client.admin.organization.audit_logs.list(\*\*params) -> SyncConversationCursorPage[AuditLogListResponse] + # [Responses](src/openai/resources/responses/api.md) # [Realtime](src/openai/resources/realtime/api.md) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 5fc22da800..cbaef0615f 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -398,6 +398,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction] from ._module_client import ( beta as beta, chat as chat, + admin as admin, audio as audio, evals as evals, files as files, diff --git a/src/openai/_client.py b/src/openai/_client.py index 2937cd89f2..283f39ce2d 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -43,6 +43,7 @@ from .resources import ( beta, chat, + admin, audio, evals, files, @@ -70,6 +71,7 @@ from .resources.beta.beta import Beta, AsyncBeta from .resources.chat.chat import Chat, AsyncChat from .resources.embeddings import Embeddings, AsyncEmbeddings + from .resources.admin.admin import Admin, AsyncAdmin from .resources.audio.audio import Audio, AsyncAudio from .resources.completions import Completions, AsyncCompletions from .resources.evals.evals import Evals, AsyncEvals @@ -324,6 +326,12 @@ def uploads(self) -> Uploads: return Uploads(self) + @cached_property + def admin(self) -> Admin: + from .resources.admin import Admin + + return Admin(self) + @cached_property def responses(self) -> Responses: from .resources.responses import Responses @@ -813,6 +821,12 @@ def uploads(self) -> AsyncUploads: return AsyncUploads(self) + @cached_property + def admin(self) -> AsyncAdmin: + from .resources.admin import AsyncAdmin + + return AsyncAdmin(self) + @cached_property def responses(self) -> AsyncResponses: from .resources.responses import AsyncResponses @@ -1166,6 +1180,12 @@ def uploads(self) -> uploads.UploadsWithRawResponse: return UploadsWithRawResponse(self._client.uploads) + @cached_property + def admin(self) -> admin.AdminWithRawResponse: + from .resources.admin import AdminWithRawResponse + + return AdminWithRawResponse(self._client.admin) + @cached_property def responses(self) -> responses.ResponsesWithRawResponse: from .resources.responses import ResponsesWithRawResponse @@ -1311,6 +1331,12 @@ def uploads(self) -> uploads.AsyncUploadsWithRawResponse: return AsyncUploadsWithRawResponse(self._client.uploads) + @cached_property + def admin(self) -> admin.AsyncAdminWithRawResponse: + from .resources.admin import AsyncAdminWithRawResponse + + return AsyncAdminWithRawResponse(self._client.admin) + @cached_property def responses(self) -> responses.AsyncResponsesWithRawResponse: from .resources.responses import AsyncResponsesWithRawResponse @@ -1456,6 +1482,12 @@ def uploads(self) -> uploads.UploadsWithStreamingResponse: return UploadsWithStreamingResponse(self._client.uploads) + @cached_property + def admin(self) -> admin.AdminWithStreamingResponse: + from .resources.admin import AdminWithStreamingResponse + + return AdminWithStreamingResponse(self._client.admin) + @cached_property def responses(self) -> responses.ResponsesWithStreamingResponse: from .resources.responses import ResponsesWithStreamingResponse @@ -1601,6 +1633,12 @@ def uploads(self) -> uploads.AsyncUploadsWithStreamingResponse: return AsyncUploadsWithStreamingResponse(self._client.uploads) + @cached_property + def admin(self) -> admin.AsyncAdminWithStreamingResponse: + from .resources.admin import AsyncAdminWithStreamingResponse + + return AsyncAdminWithStreamingResponse(self._client.admin) + @cached_property def responses(self) -> responses.AsyncResponsesWithStreamingResponse: from .resources.responses import AsyncResponsesWithStreamingResponse diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py index 98901c0446..3554e11e50 100644 --- a/src/openai/_module_client.py +++ b/src/openai/_module_client.py @@ -14,6 +14,7 @@ from .resources.beta.beta import Beta from .resources.chat.chat import Chat from .resources.embeddings import Embeddings + from .resources.admin.admin import Admin from .resources.audio.audio import Audio from .resources.completions import Completions from .resources.evals.evals import Evals @@ -56,6 +57,12 @@ def __load__(self) -> Audio: return _load_client().audio +class AdminProxy(LazyProxy["Admin"]): + @override + def __load__(self) -> Admin: + return _load_client().admin + + class EvalsProxy(LazyProxy["Evals"]): @override def __load__(self) -> Evals: @@ -162,6 +169,7 @@ def __load__(self) -> Conversations: beta: Beta = BetaProxy().__as_proxied__() files: Files = FilesProxy().__as_proxied__() audio: Audio = AudioProxy().__as_proxied__() +admin: Admin = AdminProxy().__as_proxied__() evals: Evals = EvalsProxy().__as_proxied__() images: Images = ImagesProxy().__as_proxied__() models: Models = ModelsProxy().__as_proxied__() diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py index ed030f7188..e4905152c0 100644 --- a/src/openai/resources/__init__.py +++ b/src/openai/resources/__init__.py @@ -16,6 +16,14 @@ ChatWithStreamingResponse, AsyncChatWithStreamingResponse, ) +from .admin import ( + Admin, + AsyncAdmin, + AdminWithRawResponse, + AsyncAdminWithRawResponse, + AdminWithStreamingResponse, + AsyncAdminWithStreamingResponse, +) from .audio import ( Audio, AsyncAudio, @@ -216,6 +224,12 @@ "AsyncUploadsWithRawResponse", "UploadsWithStreamingResponse", "AsyncUploadsWithStreamingResponse", + "Admin", + "AsyncAdmin", + "AdminWithRawResponse", + "AsyncAdminWithRawResponse", + "AdminWithStreamingResponse", + "AsyncAdminWithStreamingResponse", "Evals", "AsyncEvals", "EvalsWithRawResponse", diff --git a/src/openai/resources/admin/__init__.py b/src/openai/resources/admin/__init__.py new file mode 100644 index 0000000000..730c20540c --- /dev/null +++ b/src/openai/resources/admin/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .admin import ( + Admin, + AsyncAdmin, + AdminWithRawResponse, + AsyncAdminWithRawResponse, + AdminWithStreamingResponse, + AsyncAdminWithStreamingResponse, +) +from .organization import ( + Organization, + AsyncOrganization, + OrganizationWithRawResponse, + AsyncOrganizationWithRawResponse, + OrganizationWithStreamingResponse, + AsyncOrganizationWithStreamingResponse, +) + +__all__ = [ + "Organization", + "AsyncOrganization", + "OrganizationWithRawResponse", + "AsyncOrganizationWithRawResponse", + "OrganizationWithStreamingResponse", + "AsyncOrganizationWithStreamingResponse", + "Admin", + "AsyncAdmin", + "AdminWithRawResponse", + "AsyncAdminWithRawResponse", + "AdminWithStreamingResponse", + "AsyncAdminWithStreamingResponse", +] diff --git a/src/openai/resources/admin/admin.py b/src/openai/resources/admin/admin.py new file mode 100644 index 0000000000..3a3e288c6a --- /dev/null +++ b/src/openai/resources/admin/admin.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .organization.organization import ( + Organization, + AsyncOrganization, + OrganizationWithRawResponse, + AsyncOrganizationWithRawResponse, + OrganizationWithStreamingResponse, + AsyncOrganizationWithStreamingResponse, +) + +__all__ = ["Admin", "AsyncAdmin"] + + +class Admin(SyncAPIResource): + @cached_property + def organization(self) -> Organization: + return Organization(self._client) + + @cached_property + def with_raw_response(self) -> AdminWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AdminWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AdminWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AdminWithStreamingResponse(self) + + +class AsyncAdmin(AsyncAPIResource): + @cached_property + def organization(self) -> AsyncOrganization: + return AsyncOrganization(self._client) + + @cached_property + def with_raw_response(self) -> AsyncAdminWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAdminWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAdminWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAdminWithStreamingResponse(self) + + +class AdminWithRawResponse: + def __init__(self, admin: Admin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> OrganizationWithRawResponse: + return OrganizationWithRawResponse(self._admin.organization) + + +class AsyncAdminWithRawResponse: + def __init__(self, admin: AsyncAdmin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> AsyncOrganizationWithRawResponse: + return AsyncOrganizationWithRawResponse(self._admin.organization) + + +class AdminWithStreamingResponse: + def __init__(self, admin: Admin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> OrganizationWithStreamingResponse: + return OrganizationWithStreamingResponse(self._admin.organization) + + +class AsyncAdminWithStreamingResponse: + def __init__(self, admin: AsyncAdmin) -> None: + self._admin = admin + + @cached_property + def organization(self) -> AsyncOrganizationWithStreamingResponse: + return AsyncOrganizationWithStreamingResponse(self._admin.organization) diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py new file mode 100644 index 0000000000..d3e8314a44 --- /dev/null +++ b/src/openai/resources/admin/organization/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .audit_logs import ( + AuditLogs, + AsyncAuditLogs, + AuditLogsWithRawResponse, + AsyncAuditLogsWithRawResponse, + AuditLogsWithStreamingResponse, + AsyncAuditLogsWithStreamingResponse, +) +from .organization import ( + Organization, + AsyncOrganization, + OrganizationWithRawResponse, + AsyncOrganizationWithRawResponse, + OrganizationWithStreamingResponse, + AsyncOrganizationWithStreamingResponse, +) + +__all__ = [ + "AuditLogs", + "AsyncAuditLogs", + "AuditLogsWithRawResponse", + "AsyncAuditLogsWithRawResponse", + "AuditLogsWithStreamingResponse", + "AsyncAuditLogsWithStreamingResponse", + "Organization", + "AsyncOrganization", + "OrganizationWithRawResponse", + "AsyncOrganizationWithRawResponse", + "OrganizationWithStreamingResponse", + "AsyncOrganizationWithStreamingResponse", +] diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py new file mode 100644 index 0000000000..3cf3127631 --- /dev/null +++ b/src/openai/resources/admin/organization/audit_logs.py @@ -0,0 +1,387 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import audit_log_list_params +from ....types.admin.organization.audit_log_list_response import AuditLogListResponse + +__all__ = ["AuditLogs", "AsyncAuditLogs"] + + +class AuditLogs(SyncAPIResource): + """List user actions and configuration changes within this organization.""" + + @cached_property + def with_raw_response(self) -> AuditLogsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AuditLogsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AuditLogsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AuditLogsWithStreamingResponse(self) + + def list( + self, + *, + actor_emails: SequenceNotStr[str] | Omit = omit, + actor_ids: SequenceNotStr[str] | Omit = omit, + after: str | Omit = omit, + before: str | Omit = omit, + effective_at: audit_log_list_params.EffectiveAt | Omit = omit, + event_types: List[ + Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + ] + ] + | Omit = omit, + limit: int | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + resource_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[AuditLogListResponse]: + """ + List user actions and configuration changes within this organization. + + Args: + actor_emails: Return only events performed by users with these emails. + + actor_ids: Return only events performed by these actors. Can be a user ID, a service + account ID, or an api key tracking ID. + + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + effective_at: Return only events whose `effective_at` (Unix seconds) is in this range. + + event_types: Return only events with a `type` in one of these values. For example, + `project.created`. For all options, see the documentation for the + [audit log object](https://platform.openai.com/docs/api-reference/audit-logs/object). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + project_ids: Return only events for these projects. + + resource_ids: Return only events performed on these targets. For example, a project ID + updated. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/audit_logs", + page=SyncConversationCursorPage[AuditLogListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "actor_emails": actor_emails, + "actor_ids": actor_ids, + "after": after, + "before": before, + "effective_at": effective_at, + "event_types": event_types, + "limit": limit, + "project_ids": project_ids, + "resource_ids": resource_ids, + }, + audit_log_list_params.AuditLogListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AuditLogListResponse, + ) + + +class AsyncAuditLogs(AsyncAPIResource): + """List user actions and configuration changes within this organization.""" + + @cached_property + def with_raw_response(self) -> AsyncAuditLogsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAuditLogsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAuditLogsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAuditLogsWithStreamingResponse(self) + + def list( + self, + *, + actor_emails: SequenceNotStr[str] | Omit = omit, + actor_ids: SequenceNotStr[str] | Omit = omit, + after: str | Omit = omit, + before: str | Omit = omit, + effective_at: audit_log_list_params.EffectiveAt | Omit = omit, + event_types: List[ + Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + ] + ] + | Omit = omit, + limit: int | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + resource_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AuditLogListResponse, AsyncConversationCursorPage[AuditLogListResponse]]: + """ + List user actions and configuration changes within this organization. + + Args: + actor_emails: Return only events performed by users with these emails. + + actor_ids: Return only events performed by these actors. Can be a user ID, a service + account ID, or an api key tracking ID. + + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + starting with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + effective_at: Return only events whose `effective_at` (Unix seconds) is in this range. + + event_types: Return only events with a `type` in one of these values. For example, + `project.created`. For all options, see the documentation for the + [audit log object](https://platform.openai.com/docs/api-reference/audit-logs/object). + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + project_ids: Return only events for these projects. + + resource_ids: Return only events performed on these targets. For example, a project ID + updated. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/audit_logs", + page=AsyncConversationCursorPage[AuditLogListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "actor_emails": actor_emails, + "actor_ids": actor_ids, + "after": after, + "before": before, + "effective_at": effective_at, + "event_types": event_types, + "limit": limit, + "project_ids": project_ids, + "resource_ids": resource_ids, + }, + audit_log_list_params.AuditLogListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AuditLogListResponse, + ) + + +class AuditLogsWithRawResponse: + def __init__(self, audit_logs: AuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = _legacy_response.to_raw_response_wrapper( + audit_logs.list, + ) + + +class AsyncAuditLogsWithRawResponse: + def __init__(self, audit_logs: AsyncAuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = _legacy_response.async_to_raw_response_wrapper( + audit_logs.list, + ) + + +class AuditLogsWithStreamingResponse: + def __init__(self, audit_logs: AuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = to_streamed_response_wrapper( + audit_logs.list, + ) + + +class AsyncAuditLogsWithStreamingResponse: + def __init__(self, audit_logs: AsyncAuditLogs) -> None: + self._audit_logs = audit_logs + + self.list = async_to_streamed_response_wrapper( + audit_logs.list, + ) diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py new file mode 100644 index 0000000000..41dd0155d9 --- /dev/null +++ b/src/openai/resources/admin/organization/organization.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ...._compat import cached_property +from .audit_logs import ( + AuditLogs, + AsyncAuditLogs, + AuditLogsWithRawResponse, + AsyncAuditLogsWithRawResponse, + AuditLogsWithStreamingResponse, + AsyncAuditLogsWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["Organization", "AsyncOrganization"] + + +class Organization(SyncAPIResource): + @cached_property + def audit_logs(self) -> AuditLogs: + """List user actions and configuration changes within this organization.""" + return AuditLogs(self._client) + + @cached_property + def with_raw_response(self) -> OrganizationWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return OrganizationWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OrganizationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return OrganizationWithStreamingResponse(self) + + +class AsyncOrganization(AsyncAPIResource): + @cached_property + def audit_logs(self) -> AsyncAuditLogs: + """List user actions and configuration changes within this organization.""" + return AsyncAuditLogs(self._client) + + @cached_property + def with_raw_response(self) -> AsyncOrganizationWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncOrganizationWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOrganizationWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncOrganizationWithStreamingResponse(self) + + +class OrganizationWithRawResponse: + def __init__(self, organization: Organization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AuditLogsWithRawResponse: + """List user actions and configuration changes within this organization.""" + return AuditLogsWithRawResponse(self._organization.audit_logs) + + +class AsyncOrganizationWithRawResponse: + def __init__(self, organization: AsyncOrganization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AsyncAuditLogsWithRawResponse: + """List user actions and configuration changes within this organization.""" + return AsyncAuditLogsWithRawResponse(self._organization.audit_logs) + + +class OrganizationWithStreamingResponse: + def __init__(self, organization: Organization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AuditLogsWithStreamingResponse: + """List user actions and configuration changes within this organization.""" + return AuditLogsWithStreamingResponse(self._organization.audit_logs) + + +class AsyncOrganizationWithStreamingResponse: + def __init__(self, organization: AsyncOrganization) -> None: + self._organization = organization + + @cached_property + def audit_logs(self) -> AsyncAuditLogsWithStreamingResponse: + """List user actions and configuration changes within this organization.""" + return AsyncAuditLogsWithStreamingResponse(self._organization.audit_logs) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py new file mode 100644 index 0000000000..c402a94c9d --- /dev/null +++ b/src/openai/types/admin/organization/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .audit_log_list_params import AuditLogListParams as AuditLogListParams +from .audit_log_list_response import AuditLogListResponse as AuditLogListResponse diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py new file mode 100644 index 0000000000..bd3bc6d629 --- /dev/null +++ b/src/openai/types/admin/organization/audit_log_list_params.py @@ -0,0 +1,143 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["AuditLogListParams", "EffectiveAt"] + + +class AuditLogListParams(TypedDict, total=False): + actor_emails: SequenceNotStr[str] + """Return only events performed by users with these emails.""" + + actor_ids: SequenceNotStr[str] + """Return only events performed by these actors. + + Can be a user ID, a service account ID, or an api key tracking ID. + """ + + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, starting with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + effective_at: EffectiveAt + """Return only events whose `effective_at` (Unix seconds) is in this range.""" + + event_types: List[ + Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + ] + ] + """Return only events with a `type` in one of these values. + + For example, `project.created`. For all options, see the documentation for the + [audit log object](https://platform.openai.com/docs/api-reference/audit-logs/object). + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + project_ids: SequenceNotStr[str] + """Return only events for these projects.""" + + resource_ids: SequenceNotStr[str] + """Return only events performed on these targets. + + For example, a project ID updated. + """ + + +class EffectiveAt(TypedDict, total=False): + """Return only events whose `effective_at` (Unix seconds) is in this range.""" + + gt: int + """ + Return only events whose `effective_at` (Unix seconds) is greater than this + value. + """ + + gte: int + """ + Return only events whose `effective_at` (Unix seconds) is greater than or equal + to this value. + """ + + lt: int + """Return only events whose `effective_at` (Unix seconds) is less than this value.""" + + lte: int + """ + Return only events whose `effective_at` (Unix seconds) is less than or equal to + this value. + """ diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py new file mode 100644 index 0000000000..db4d20b9ef --- /dev/null +++ b/src/openai/types/admin/organization/audit_log_list_response.py @@ -0,0 +1,1033 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = [ + "AuditLogListResponse", + "Actor", + "ActorAPIKey", + "ActorAPIKeyServiceAccount", + "ActorAPIKeyUser", + "ActorSession", + "ActorSessionUser", + "APIKeyCreated", + "APIKeyCreatedData", + "APIKeyDeleted", + "APIKeyUpdated", + "APIKeyUpdatedChangesRequested", + "CertificateCreated", + "CertificateDeleted", + "CertificateUpdated", + "CertificatesActivated", + "CertificatesActivatedCertificate", + "CertificatesDeactivated", + "CertificatesDeactivatedCertificate", + "CheckpointPermissionCreated", + "CheckpointPermissionCreatedData", + "CheckpointPermissionDeleted", + "ExternalKeyRegistered", + "ExternalKeyRemoved", + "GroupCreated", + "GroupCreatedData", + "GroupDeleted", + "GroupUpdated", + "GroupUpdatedChangesRequested", + "InviteAccepted", + "InviteDeleted", + "InviteSent", + "InviteSentData", + "IPAllowlistConfigActivated", + "IPAllowlistConfigActivatedConfig", + "IPAllowlistConfigDeactivated", + "IPAllowlistConfigDeactivatedConfig", + "IPAllowlistCreated", + "IPAllowlistDeleted", + "IPAllowlistUpdated", + "LoginFailed", + "LogoutFailed", + "OrganizationUpdated", + "OrganizationUpdatedChangesRequested", + "Project", + "ProjectArchived", + "ProjectCreated", + "ProjectCreatedData", + "ProjectDeleted", + "ProjectUpdated", + "ProjectUpdatedChangesRequested", + "RateLimitDeleted", + "RateLimitUpdated", + "RateLimitUpdatedChangesRequested", + "RoleAssignmentCreated", + "RoleAssignmentDeleted", + "RoleCreated", + "RoleDeleted", + "RoleUpdated", + "RoleUpdatedChangesRequested", + "ScimDisabled", + "ScimEnabled", + "ServiceAccountCreated", + "ServiceAccountCreatedData", + "ServiceAccountDeleted", + "ServiceAccountUpdated", + "ServiceAccountUpdatedChangesRequested", + "UserAdded", + "UserAddedData", + "UserDeleted", + "UserUpdated", + "UserUpdatedChangesRequested", +] + + +class ActorAPIKeyServiceAccount(BaseModel): + """The service account that performed the audit logged action.""" + + id: Optional[str] = None + """The service account id.""" + + +class ActorAPIKeyUser(BaseModel): + """The user who performed the audit logged action.""" + + id: Optional[str] = None + """The user id.""" + + email: Optional[str] = None + """The user email.""" + + +class ActorAPIKey(BaseModel): + """The API Key used to perform the audit logged action.""" + + id: Optional[str] = None + """The tracking id of the API key.""" + + service_account: Optional[ActorAPIKeyServiceAccount] = None + """The service account that performed the audit logged action.""" + + type: Optional[Literal["user", "service_account"]] = None + """The type of API key. Can be either `user` or `service_account`.""" + + user: Optional[ActorAPIKeyUser] = None + """The user who performed the audit logged action.""" + + +class ActorSessionUser(BaseModel): + """The user who performed the audit logged action.""" + + id: Optional[str] = None + """The user id.""" + + email: Optional[str] = None + """The user email.""" + + +class ActorSession(BaseModel): + """The session in which the audit logged action was performed.""" + + ip_address: Optional[str] = None + """The IP address from which the action was performed.""" + + user: Optional[ActorSessionUser] = None + """The user who performed the audit logged action.""" + + +class Actor(BaseModel): + """The actor who performed the audit logged action.""" + + api_key: Optional[ActorAPIKey] = None + """The API Key used to perform the audit logged action.""" + + session: Optional[ActorSession] = None + """The session in which the audit logged action was performed.""" + + type: Optional[Literal["session", "api_key"]] = None + """The type of actor. Is either `session` or `api_key`.""" + + +class APIKeyCreatedData(BaseModel): + """The payload used to create the API key.""" + + scopes: Optional[List[str]] = None + """A list of scopes allowed for the API key, e.g. `["api.model.request"]`""" + + +class APIKeyCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The tracking ID of the API key.""" + + data: Optional[APIKeyCreatedData] = None + """The payload used to create the API key.""" + + +class APIKeyDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The tracking ID of the API key.""" + + +class APIKeyUpdatedChangesRequested(BaseModel): + """The payload used to update the API key.""" + + scopes: Optional[List[str]] = None + """A list of scopes allowed for the API key, e.g. `["api.model.request"]`""" + + +class APIKeyUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The tracking ID of the API key.""" + + changes_requested: Optional[APIKeyUpdatedChangesRequested] = None + """The payload used to update the API key.""" + + +class CertificateCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificateDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The certificate ID.""" + + certificate: Optional[str] = None + """The certificate content in PEM format.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificateUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificatesActivatedCertificate(BaseModel): + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificatesActivated(BaseModel): + """The details for events with this `type`.""" + + certificates: Optional[List[CertificatesActivatedCertificate]] = None + + +class CertificatesDeactivatedCertificate(BaseModel): + id: Optional[str] = None + """The certificate ID.""" + + name: Optional[str] = None + """The name of the certificate.""" + + +class CertificatesDeactivated(BaseModel): + """The details for events with this `type`.""" + + certificates: Optional[List[CertificatesDeactivatedCertificate]] = None + + +class CheckpointPermissionCreatedData(BaseModel): + """The payload used to create the checkpoint permission.""" + + fine_tuned_model_checkpoint: Optional[str] = None + """The ID of the fine-tuned model checkpoint.""" + + project_id: Optional[str] = None + """The ID of the project that the checkpoint permission was created for.""" + + +class CheckpointPermissionCreated(BaseModel): + """ + The project and fine-tuned model checkpoint that the checkpoint permission was created for. + """ + + id: Optional[str] = None + """The ID of the checkpoint permission.""" + + data: Optional[CheckpointPermissionCreatedData] = None + """The payload used to create the checkpoint permission.""" + + +class CheckpointPermissionDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the checkpoint permission.""" + + +class ExternalKeyRegistered(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the external key configuration.""" + + data: Optional[object] = None + """The configuration for the external key.""" + + +class ExternalKeyRemoved(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the external key configuration.""" + + +class GroupCreatedData(BaseModel): + """Information about the created group.""" + + group_name: Optional[str] = None + """The group name.""" + + +class GroupCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the group.""" + + data: Optional[GroupCreatedData] = None + """Information about the created group.""" + + +class GroupDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the group.""" + + +class GroupUpdatedChangesRequested(BaseModel): + """The payload used to update the group.""" + + group_name: Optional[str] = None + """The updated group name.""" + + +class GroupUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the group.""" + + changes_requested: Optional[GroupUpdatedChangesRequested] = None + """The payload used to update the group.""" + + +class InviteAccepted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the invite.""" + + +class InviteDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the invite.""" + + +class InviteSentData(BaseModel): + """The payload used to create the invite.""" + + email: Optional[str] = None + """The email invited to the organization.""" + + role: Optional[str] = None + """The role the email was invited to be. Is either `owner` or `member`.""" + + +class InviteSent(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the invite.""" + + data: Optional[InviteSentData] = None + """The payload used to create the invite.""" + + +class IPAllowlistConfigActivatedConfig(BaseModel): + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistConfigActivated(BaseModel): + """The details for events with this `type`.""" + + configs: Optional[List[IPAllowlistConfigActivatedConfig]] = None + """The configurations that were activated.""" + + +class IPAllowlistConfigDeactivatedConfig(BaseModel): + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistConfigDeactivated(BaseModel): + """The details for events with this `type`.""" + + configs: Optional[List[IPAllowlistConfigDeactivatedConfig]] = None + """The configurations that were deactivated.""" + + +class IPAllowlistCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + allowed_ips: Optional[List[str]] = None + """The IP addresses or CIDR ranges included in the configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + allowed_ips: Optional[List[str]] = None + """The IP addresses or CIDR ranges that were in the configuration.""" + + name: Optional[str] = None + """The name of the IP allowlist configuration.""" + + +class IPAllowlistUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the IP allowlist configuration.""" + + allowed_ips: Optional[List[str]] = None + """The updated set of IP addresses or CIDR ranges in the configuration.""" + + +class LoginFailed(BaseModel): + """The details for events with this `type`.""" + + error_code: Optional[str] = None + """The error code of the failure.""" + + error_message: Optional[str] = None + """The error message of the failure.""" + + +class LogoutFailed(BaseModel): + """The details for events with this `type`.""" + + error_code: Optional[str] = None + """The error code of the failure.""" + + error_message: Optional[str] = None + """The error message of the failure.""" + + +class OrganizationUpdatedChangesRequested(BaseModel): + """The payload used to update the organization settings.""" + + api_call_logging: Optional[str] = None + """How your organization logs data from supported API calls. + + One of `disabled`, `enabled_per_call`, `enabled_for_all_projects`, or + `enabled_for_selected_projects` + """ + + api_call_logging_project_ids: Optional[str] = None + """ + The list of project ids if api_call_logging is set to + `enabled_for_selected_projects` + """ + + description: Optional[str] = None + """The organization description.""" + + name: Optional[str] = None + """The organization name.""" + + threads_ui_visibility: Optional[str] = None + """ + Visibility of the threads page which shows messages created with the Assistants + API and Playground. One of `ANY_ROLE`, `OWNERS`, or `NONE`. + """ + + title: Optional[str] = None + """The organization title.""" + + usage_dashboard_visibility: Optional[str] = None + """ + Visibility of the usage dashboard which shows activity and costs for your + organization. One of `ANY_ROLE` or `OWNERS`. + """ + + +class OrganizationUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The organization ID.""" + + changes_requested: Optional[OrganizationUpdatedChangesRequested] = None + """The payload used to update the organization settings.""" + + +class Project(BaseModel): + """The project that the action was scoped to. + + Absent for actions not scoped to projects. Note that any admin actions taken via Admin API keys are associated with the default project. + """ + + id: Optional[str] = None + """The project ID.""" + + name: Optional[str] = None + """The project title.""" + + +class ProjectArchived(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + +class ProjectCreatedData(BaseModel): + """The payload used to create the project.""" + + name: Optional[str] = None + """The project name.""" + + title: Optional[str] = None + """The title of the project as seen on the dashboard.""" + + +class ProjectCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + data: Optional[ProjectCreatedData] = None + """The payload used to create the project.""" + + +class ProjectDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + +class ProjectUpdatedChangesRequested(BaseModel): + """The payload used to update the project.""" + + title: Optional[str] = None + """The title of the project as seen on the dashboard.""" + + +class ProjectUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + changes_requested: Optional[ProjectUpdatedChangesRequested] = None + """The payload used to update the project.""" + + +class RateLimitDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The rate limit ID""" + + +class RateLimitUpdatedChangesRequested(BaseModel): + """The payload used to update the rate limits.""" + + batch_1_day_max_input_tokens: Optional[int] = None + """The maximum batch input tokens per day. Only relevant for certain models.""" + + max_audio_megabytes_per_1_minute: Optional[int] = None + """The maximum audio megabytes per minute. Only relevant for certain models.""" + + max_images_per_1_minute: Optional[int] = None + """The maximum images per minute. Only relevant for certain models.""" + + max_requests_per_1_day: Optional[int] = None + """The maximum requests per day. Only relevant for certain models.""" + + max_requests_per_1_minute: Optional[int] = None + """The maximum requests per minute.""" + + max_tokens_per_1_minute: Optional[int] = None + """The maximum tokens per minute.""" + + +class RateLimitUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The rate limit ID""" + + changes_requested: Optional[RateLimitUpdatedChangesRequested] = None + """The payload used to update the rate limits.""" + + +class RoleAssignmentCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The identifier of the role assignment.""" + + principal_id: Optional[str] = None + """The principal (user or group) that received the role.""" + + principal_type: Optional[str] = None + """The type of principal (user or group) that received the role.""" + + resource_id: Optional[str] = None + """The resource the role assignment is scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role assignment is scoped to.""" + + +class RoleAssignmentDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The identifier of the role assignment.""" + + principal_id: Optional[str] = None + """The principal (user or group) that had the role removed.""" + + principal_type: Optional[str] = None + """The type of principal (user or group) that had the role removed.""" + + resource_id: Optional[str] = None + """The resource the role assignment was scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role assignment was scoped to.""" + + +class RoleCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The role ID.""" + + permissions: Optional[List[str]] = None + """The permissions granted by the role.""" + + resource_id: Optional[str] = None + """The resource the role is scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role belongs to.""" + + role_name: Optional[str] = None + """The name of the role.""" + + +class RoleDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The role ID.""" + + +class RoleUpdatedChangesRequested(BaseModel): + """The payload used to update the role.""" + + description: Optional[str] = None + """The updated role description, when provided.""" + + metadata: Optional[object] = None + """Additional metadata stored on the role.""" + + permissions_added: Optional[List[str]] = None + """The permissions added to the role.""" + + permissions_removed: Optional[List[str]] = None + """The permissions removed from the role.""" + + resource_id: Optional[str] = None + """The resource the role is scoped to.""" + + resource_type: Optional[str] = None + """The type of resource the role belongs to.""" + + role_name: Optional[str] = None + """The updated role name, when provided.""" + + +class RoleUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The role ID.""" + + changes_requested: Optional[RoleUpdatedChangesRequested] = None + """The payload used to update the role.""" + + +class ScimDisabled(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the SCIM was disabled for.""" + + +class ScimEnabled(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the SCIM was enabled for.""" + + +class ServiceAccountCreatedData(BaseModel): + """The payload used to create the service account.""" + + role: Optional[str] = None + """The role of the service account. Is either `owner` or `member`.""" + + +class ServiceAccountCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The service account ID.""" + + data: Optional[ServiceAccountCreatedData] = None + """The payload used to create the service account.""" + + +class ServiceAccountDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The service account ID.""" + + +class ServiceAccountUpdatedChangesRequested(BaseModel): + """The payload used to updated the service account.""" + + role: Optional[str] = None + """The role of the service account. Is either `owner` or `member`.""" + + +class ServiceAccountUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The service account ID.""" + + changes_requested: Optional[ServiceAccountUpdatedChangesRequested] = None + """The payload used to updated the service account.""" + + +class UserAddedData(BaseModel): + """The payload used to add the user to the project.""" + + role: Optional[str] = None + """The role of the user. Is either `owner` or `member`.""" + + +class UserAdded(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The user ID.""" + + data: Optional[UserAddedData] = None + """The payload used to add the user to the project.""" + + +class UserDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The user ID.""" + + +class UserUpdatedChangesRequested(BaseModel): + """The payload used to update the user.""" + + role: Optional[str] = None + """The role of the user. Is either `owner` or `member`.""" + + +class UserUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The project ID.""" + + changes_requested: Optional[UserUpdatedChangesRequested] = None + """The payload used to update the user.""" + + +class AuditLogListResponse(BaseModel): + """A log of a user action or configuration change within this organization.""" + + id: str + """The ID of this log.""" + + actor: Actor + """The actor who performed the audit logged action.""" + + effective_at: int + """The Unix timestamp (in seconds) of the event.""" + + type: Literal[ + "api_key.created", + "api_key.updated", + "api_key.deleted", + "certificate.created", + "certificate.updated", + "certificate.deleted", + "certificates.activated", + "certificates.deactivated", + "checkpoint.permission.created", + "checkpoint.permission.deleted", + "external_key.registered", + "external_key.removed", + "group.created", + "group.updated", + "group.deleted", + "invite.sent", + "invite.accepted", + "invite.deleted", + "ip_allowlist.created", + "ip_allowlist.updated", + "ip_allowlist.deleted", + "ip_allowlist.config.activated", + "ip_allowlist.config.deactivated", + "login.succeeded", + "login.failed", + "logout.succeeded", + "logout.failed", + "organization.updated", + "project.created", + "project.updated", + "project.archived", + "project.deleted", + "rate_limit.updated", + "rate_limit.deleted", + "resource.deleted", + "tunnel.created", + "tunnel.updated", + "tunnel.deleted", + "role.created", + "role.updated", + "role.deleted", + "role.assignment.created", + "role.assignment.deleted", + "scim.enabled", + "scim.disabled", + "service_account.created", + "service_account.updated", + "service_account.deleted", + "user.added", + "user.updated", + "user.deleted", + ] + """The event type.""" + + api_key_created: Optional[APIKeyCreated] = FieldInfo(alias="api_key.created", default=None) + """The details for events with this `type`.""" + + api_key_deleted: Optional[APIKeyDeleted] = FieldInfo(alias="api_key.deleted", default=None) + """The details for events with this `type`.""" + + api_key_updated: Optional[APIKeyUpdated] = FieldInfo(alias="api_key.updated", default=None) + """The details for events with this `type`.""" + + certificate_created: Optional[CertificateCreated] = FieldInfo(alias="certificate.created", default=None) + """The details for events with this `type`.""" + + certificate_deleted: Optional[CertificateDeleted] = FieldInfo(alias="certificate.deleted", default=None) + """The details for events with this `type`.""" + + certificate_updated: Optional[CertificateUpdated] = FieldInfo(alias="certificate.updated", default=None) + """The details for events with this `type`.""" + + certificates_activated: Optional[CertificatesActivated] = FieldInfo(alias="certificates.activated", default=None) + """The details for events with this `type`.""" + + certificates_deactivated: Optional[CertificatesDeactivated] = FieldInfo( + alias="certificates.deactivated", default=None + ) + """The details for events with this `type`.""" + + checkpoint_permission_created: Optional[CheckpointPermissionCreated] = FieldInfo( + alias="checkpoint.permission.created", default=None + ) + """ + The project and fine-tuned model checkpoint that the checkpoint permission was + created for. + """ + + checkpoint_permission_deleted: Optional[CheckpointPermissionDeleted] = FieldInfo( + alias="checkpoint.permission.deleted", default=None + ) + """The details for events with this `type`.""" + + external_key_registered: Optional[ExternalKeyRegistered] = FieldInfo(alias="external_key.registered", default=None) + """The details for events with this `type`.""" + + external_key_removed: Optional[ExternalKeyRemoved] = FieldInfo(alias="external_key.removed", default=None) + """The details for events with this `type`.""" + + group_created: Optional[GroupCreated] = FieldInfo(alias="group.created", default=None) + """The details for events with this `type`.""" + + group_deleted: Optional[GroupDeleted] = FieldInfo(alias="group.deleted", default=None) + """The details for events with this `type`.""" + + group_updated: Optional[GroupUpdated] = FieldInfo(alias="group.updated", default=None) + """The details for events with this `type`.""" + + invite_accepted: Optional[InviteAccepted] = FieldInfo(alias="invite.accepted", default=None) + """The details for events with this `type`.""" + + invite_deleted: Optional[InviteDeleted] = FieldInfo(alias="invite.deleted", default=None) + """The details for events with this `type`.""" + + invite_sent: Optional[InviteSent] = FieldInfo(alias="invite.sent", default=None) + """The details for events with this `type`.""" + + ip_allowlist_config_activated: Optional[IPAllowlistConfigActivated] = FieldInfo( + alias="ip_allowlist.config.activated", default=None + ) + """The details for events with this `type`.""" + + ip_allowlist_config_deactivated: Optional[IPAllowlistConfigDeactivated] = FieldInfo( + alias="ip_allowlist.config.deactivated", default=None + ) + """The details for events with this `type`.""" + + ip_allowlist_created: Optional[IPAllowlistCreated] = FieldInfo(alias="ip_allowlist.created", default=None) + """The details for events with this `type`.""" + + ip_allowlist_deleted: Optional[IPAllowlistDeleted] = FieldInfo(alias="ip_allowlist.deleted", default=None) + """The details for events with this `type`.""" + + ip_allowlist_updated: Optional[IPAllowlistUpdated] = FieldInfo(alias="ip_allowlist.updated", default=None) + """The details for events with this `type`.""" + + login_failed: Optional[LoginFailed] = FieldInfo(alias="login.failed", default=None) + """The details for events with this `type`.""" + + login_succeeded: Optional[object] = FieldInfo(alias="login.succeeded", default=None) + """This event has no additional fields beyond the standard audit log attributes.""" + + logout_failed: Optional[LogoutFailed] = FieldInfo(alias="logout.failed", default=None) + """The details for events with this `type`.""" + + logout_succeeded: Optional[object] = FieldInfo(alias="logout.succeeded", default=None) + """This event has no additional fields beyond the standard audit log attributes.""" + + organization_updated: Optional[OrganizationUpdated] = FieldInfo(alias="organization.updated", default=None) + """The details for events with this `type`.""" + + project: Optional[Project] = None + """The project that the action was scoped to. + + Absent for actions not scoped to projects. Note that any admin actions taken via + Admin API keys are associated with the default project. + """ + + project_archived: Optional[ProjectArchived] = FieldInfo(alias="project.archived", default=None) + """The details for events with this `type`.""" + + project_created: Optional[ProjectCreated] = FieldInfo(alias="project.created", default=None) + """The details for events with this `type`.""" + + project_deleted: Optional[ProjectDeleted] = FieldInfo(alias="project.deleted", default=None) + """The details for events with this `type`.""" + + project_updated: Optional[ProjectUpdated] = FieldInfo(alias="project.updated", default=None) + """The details for events with this `type`.""" + + rate_limit_deleted: Optional[RateLimitDeleted] = FieldInfo(alias="rate_limit.deleted", default=None) + """The details for events with this `type`.""" + + rate_limit_updated: Optional[RateLimitUpdated] = FieldInfo(alias="rate_limit.updated", default=None) + """The details for events with this `type`.""" + + role_assignment_created: Optional[RoleAssignmentCreated] = FieldInfo(alias="role.assignment.created", default=None) + """The details for events with this `type`.""" + + role_assignment_deleted: Optional[RoleAssignmentDeleted] = FieldInfo(alias="role.assignment.deleted", default=None) + """The details for events with this `type`.""" + + role_created: Optional[RoleCreated] = FieldInfo(alias="role.created", default=None) + """The details for events with this `type`.""" + + role_deleted: Optional[RoleDeleted] = FieldInfo(alias="role.deleted", default=None) + """The details for events with this `type`.""" + + role_updated: Optional[RoleUpdated] = FieldInfo(alias="role.updated", default=None) + """The details for events with this `type`.""" + + scim_disabled: Optional[ScimDisabled] = FieldInfo(alias="scim.disabled", default=None) + """The details for events with this `type`.""" + + scim_enabled: Optional[ScimEnabled] = FieldInfo(alias="scim.enabled", default=None) + """The details for events with this `type`.""" + + service_account_created: Optional[ServiceAccountCreated] = FieldInfo(alias="service_account.created", default=None) + """The details for events with this `type`.""" + + service_account_deleted: Optional[ServiceAccountDeleted] = FieldInfo(alias="service_account.deleted", default=None) + """The details for events with this `type`.""" + + service_account_updated: Optional[ServiceAccountUpdated] = FieldInfo(alias="service_account.updated", default=None) + """The details for events with this `type`.""" + + user_added: Optional[UserAdded] = FieldInfo(alias="user.added", default=None) + """The details for events with this `type`.""" + + user_deleted: Optional[UserDeleted] = FieldInfo(alias="user.deleted", default=None) + """The details for events with this `type`.""" + + user_updated: Optional[UserUpdated] = FieldInfo(alias="user.updated", default=None) + """The details for events with this `type`.""" diff --git a/tests/api_resources/admin/__init__.py b/tests/api_resources/admin/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/__init__.py b/tests/api_resources/admin/organization/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/organization/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/test_audit_logs.py b/tests/api_resources/admin/organization/test_audit_logs.py new file mode 100644 index 0000000000..5e696461fa --- /dev/null +++ b/tests/api_resources/admin/organization/test_audit_logs.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import AuditLogListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAuditLogs: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + audit_log = client.admin.organization.audit_logs.list() + assert_matches_type(SyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + audit_log = client.admin.organization.audit_logs.list( + actor_emails=["string"], + actor_ids=["string"], + after="after", + before="before", + effective_at={ + "gt": 0, + "gte": 0, + "lt": 0, + "lte": 0, + }, + event_types=["api_key.created"], + limit=0, + project_ids=["string"], + resource_ids=["string"], + ) + assert_matches_type(SyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.audit_logs.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + audit_log = response.parse() + assert_matches_type(SyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.audit_logs.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + audit_log = response.parse() + assert_matches_type(SyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncAuditLogs: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + audit_log = await async_client.admin.organization.audit_logs.list() + assert_matches_type(AsyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + audit_log = await async_client.admin.organization.audit_logs.list( + actor_emails=["string"], + actor_ids=["string"], + after="after", + before="before", + effective_at={ + "gt": 0, + "gte": 0, + "lt": 0, + "lte": 0, + }, + event_types=["api_key.created"], + limit=0, + project_ids=["string"], + resource_ids=["string"], + ) + assert_matches_type(AsyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.audit_logs.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + audit_log = response.parse() + assert_matches_type(AsyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.audit_logs.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + audit_log = await response.parse() + assert_matches_type(AsyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) + + assert cast(Any, response.is_closed) is True From e459519520e122afe0047cc6a1e3f42f69bab8ee Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:21:01 +0000 Subject: [PATCH 323/408] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 838ad705ab..73d7722354 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 153 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-617ac7ff7388ed68d19955b74d6dd1a3e2313e5bc47eb7c7a138162e29e23b71.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-7712c92246b94f811f6e3d33569242e75b22497d51df8cc27b16a6a205b4560a.yml openapi_spec_hash: d431cf9d2bf2b01122bb98cc7b7a357b config_hash: 88be7d18ee7f33affcdad19572cd0c91 From 36752787b3800df3808f67f74d640906ac3e5536 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 30 Apr 2026 10:40:27 -0400 Subject: [PATCH 324/408] fix: require bearer auth for stream helpers --- .../resources/beta/threads/runs/runs.py | 26 ++++++++++++++++--- src/openai/resources/beta/threads/threads.py | 12 +++++++-- .../resources/chat/completions/completions.py | 2 ++ src/openai/resources/embeddings.py | 1 + src/openai/resources/responses/responses.py | 2 ++ 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 263bc787c2..385d4c680c 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -1037,7 +1037,11 @@ def create_and_stream( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, stream=True, @@ -1229,6 +1233,7 @@ def stream( extra_body=extra_body, timeout=timeout, query=maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, ), cast_to=Run, stream=True, @@ -1527,7 +1532,11 @@ def submit_tool_outputs_stream( run_submit_tool_outputs_params.RunSubmitToolOutputsParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, stream=True, @@ -2513,7 +2522,11 @@ def create_and_stream( run_create_params.RunCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, stream=True, @@ -2706,6 +2719,7 @@ def stream( extra_body=extra_body, timeout=timeout, query=maybe_transform({"include": include}, run_create_params.RunCreateParams), + security={"bearer_auth": True}, ), cast_to=Run, stream=True, @@ -3006,7 +3020,11 @@ def submit_tool_outputs_stream( run_submit_tool_outputs_params.RunSubmitToolOutputsParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, stream=True, diff --git a/src/openai/resources/beta/threads/threads.py b/src/openai/resources/beta/threads/threads.py index dec6f31a38..d9425c64bd 100644 --- a/src/openai/resources/beta/threads/threads.py +++ b/src/openai/resources/beta/threads/threads.py @@ -933,7 +933,11 @@ def create_and_run_stream( thread_create_and_run_params.ThreadCreateAndRunParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, stream=True, @@ -1820,7 +1824,11 @@ def create_and_run_stream( thread_create_and_run_params.ThreadCreateAndRunParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, ), cast_to=Run, stream=True, diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 4158f032ee..7a551e2459 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -236,6 +236,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma extra_body=extra_body, timeout=timeout, post_parser=parser, + security={"bearer_auth": True}, ), # we turn the `ChatCompletion` instance into a `ParsedChatCompletion` # in the `parser` function above @@ -1756,6 +1757,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma extra_body=extra_body, timeout=timeout, post_parser=parser, + security={"bearer_auth": True}, ), # we turn the `ChatCompletion` instance into a `ParsedChatCompletion` # in the `parser` function above diff --git a/src/openai/resources/embeddings.py b/src/openai/resources/embeddings.py index fea6255c0d..a51936d809 100644 --- a/src/openai/resources/embeddings.py +++ b/src/openai/resources/embeddings.py @@ -142,6 +142,7 @@ def parser(obj: CreateEmbeddingResponse) -> CreateEmbeddingResponse: extra_body=extra_body, timeout=timeout, post_parser=parser, + security={"bearer_auth": True}, ), cast_to=CreateEmbeddingResponse, ) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 89a8ceffa1..c0f9855bcf 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1275,6 +1275,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: extra_body=extra_body, timeout=timeout, post_parser=parser, + security={"bearer_auth": True}, ), # we turn the `Response` instance into a `ParsedResponse` # in the `parser` function above @@ -2977,6 +2978,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: extra_body=extra_body, timeout=timeout, post_parser=parser, + security={"bearer_auth": True}, ), # we turn the `Response` instance into a `ParsedResponse` # in the `parser` function above From c99535c716f90a1e10cc40a8618103ba1c5c2e49 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 30 Apr 2026 10:48:01 -0400 Subject: [PATCH 325/408] fix: preserve selected auth credentials --- src/openai/_client.py | 48 ++++++++++++++--- src/openai/lib/azure.py | 4 +- tests/lib/test_azure.py | 51 ++++++++++++++++++ tests/test_client.py | 114 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 10 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index 283f39ce2d..f999aaab15 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -91,6 +91,16 @@ WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER = "workload-identity-auth" +def _has_header(headers: Headers, header: str) -> bool: + header = header.lower() + return any(key.lower() == header for key in headers) + + +def _has_omitted_header(headers: Headers, header: str) -> bool: + header = header.lower() + return any(key.lower() == header and isinstance(value, Omit) for key, value in headers.items()) + + class OpenAI(SyncAPIClient): # client options api_key: str @@ -397,14 +407,25 @@ def _send_with_auth_retry( retried: bool = False, **kwargs: Unpack[HttpxSendArgs], ) -> httpx.Response: + used_workload_identity_auth = False if self._api_key_provider is not None: - request.headers["Authorization"] = f"Bearer {self._refresh_api_key()}" + authorization = request.headers.get("Authorization") + if authorization is None or authorization == f"Bearer {self.api_key}": + request.headers["Authorization"] = f"Bearer {self._refresh_api_key()}" if self._workload_identity_auth is not None: - request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" + authorization = request.headers.get("Authorization") + if authorization is None or authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": + request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" + used_workload_identity_auth = True response = super()._send_request(request, stream=stream, **kwargs) - if response.status_code == 401 and self._workload_identity_auth is not None and not retried: + if ( + response.status_code == 401 + and self._workload_identity_auth is not None + and used_workload_identity_auth + and not retried + ): response.close() self._workload_identity_auth.invalidate_token() request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" @@ -469,7 +490,7 @@ def default_headers(self) -> dict[str, str | Omit]: @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return if self._api_key_provider is not None or self._workload_identity_auth is not None: @@ -892,14 +913,25 @@ async def _send_with_auth_retry( retried: bool = False, **kwargs: Unpack[HttpxSendArgs], ) -> httpx.Response: + used_workload_identity_auth = False if self._api_key_provider is not None: - request.headers["Authorization"] = f"Bearer {await self._refresh_api_key()}" + authorization = request.headers.get("Authorization") + if authorization is None or authorization == f"Bearer {self.api_key}": + request.headers["Authorization"] = f"Bearer {await self._refresh_api_key()}" if self._workload_identity_auth is not None: - request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" + authorization = request.headers.get("Authorization") + if authorization is None or authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": + request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" + used_workload_identity_auth = True response = await super()._send_request(request, stream=stream, **kwargs) - if response.status_code == 401 and self._workload_identity_auth is not None and not retried: + if ( + response.status_code == 401 + and self._workload_identity_auth is not None + and used_workload_identity_auth + and not retried + ): await response.aclose() self._workload_identity_auth.invalidate_token() request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" @@ -964,7 +996,7 @@ def default_headers(self) -> dict[str, str | Omit]: @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return if self._api_key_provider is not None or self._workload_identity_auth is not None: diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index a43316df98..e0b0956334 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -206,7 +206,7 @@ def __init__( if azure_ad_token is None: azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN") - if api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + if _enforce_credentials and api_key is None and azure_ad_token is None and azure_ad_token_provider is None: raise OpenAIError( "Missing credentials. Please pass one of `api_key`, `azure_ad_token`, `azure_ad_token_provider`, or the `AZURE_OPENAI_API_KEY` or `AZURE_OPENAI_AD_TOKEN` environment variables." ) @@ -505,7 +505,7 @@ def __init__( if azure_ad_token is None: azure_ad_token = os.environ.get("AZURE_OPENAI_AD_TOKEN") - if api_key is None and azure_ad_token is None and azure_ad_token_provider is None: + if _enforce_credentials and api_key is None and azure_ad_token is None and azure_ad_token_provider is None: raise OpenAIError( "Missing credentials. Please pass one of `api_key`, `azure_ad_token`, `azure_ad_token_provider`, or the `AZURE_OPENAI_API_KEY` or `AZURE_OPENAI_AD_TOKEN` environment variables." ) diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py index 52c24eba27..17ee885ed4 100644 --- a/tests/lib/test_azure.py +++ b/tests/lib/test_azure.py @@ -8,6 +8,9 @@ import pytest from respx import MockRouter +from openai import OpenAIError +from tests.utils import update_env +from openai._types import Omit from openai._utils import SensitiveHeadersFilter, is_dict from openai._models import FinalRequestOptions from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI @@ -76,6 +79,54 @@ def test_client_copying_override_options(client: Client) -> None: assert copied._custom_query == {"api-version": "2022-05-01"} +def test_enforce_credentials_false_sync() -> None: + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + AzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + _enforce_credentials=False, + ) + + +def test_enforce_credentials_true_sync() -> None: + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + with pytest.raises(OpenAIError, match="Missing credentials"): + AzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + ) + + +def test_enforce_credentials_false_async() -> None: + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + _enforce_credentials=False, + ) + + +def test_enforce_credentials_true_async() -> None: + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + with pytest.raises(OpenAIError, match="Missing credentials"): + AsyncAzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + ) + + @pytest.mark.respx() def test_client_token_provider_refresh_sync(respx_mock: MockRouter) -> None: respx_mock.post( diff --git a/tests/test_client.py b/tests/test_client.py index 3f3288158f..99cd5c3b34 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -492,6 +492,29 @@ def test_validate_headers(self) -> None: ) ) + with update_env( + **{ + "OPENAI_API_KEY": Omit(), + "OPENAI_ADMIN_KEY": Omit(), + } + ): + no_credentials = OpenAI( + base_url=base_url, + api_key=None, + admin_api_key=None, + _enforce_credentials=False, + _strict_response_validation=True, + ) + lowercase_auth_request = no_credentials._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"authorization": "Bearer custom"}) + ) + assert lowercase_auth_request.headers.get("Authorization") == "Bearer custom" + + omitted_auth_request = no_credentials._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"authorization": Omit()}) + ) + assert "Authorization" not in omitted_auth_request.headers + with update_env( **{ "OPENAI_API_KEY": Omit(), @@ -501,6 +524,40 @@ def test_validate_headers(self) -> None: with pytest.raises(OpenAIError, match="Missing credentials"): OpenAI(base_url=base_url, api_key=None, admin_api_key=None, _strict_response_validation=True) + @pytest.mark.respx(base_url=base_url) + def test_api_key_provider_preserves_admin_auth(self, respx_mock: MockRouter) -> None: + respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True})) + + provider_called = False + + def api_key_provider() -> str: + nonlocal provider_called + provider_called = True + return "dynamic-api-key" + + client = OpenAI(base_url=base_url, api_key=api_key_provider, admin_api_key=admin_api_key) + response = client.get( + "/organization/projects", + cast_to=httpx.Response, + options={"security": {"admin_api_key_auth": True}}, + ) + + assert response.request.headers.get("Authorization") == f"Bearer {admin_api_key}" + assert provider_called is False + + @pytest.mark.respx(base_url=base_url) + def test_workload_identity_preserves_admin_auth(self, respx_mock: MockRouter) -> None: + respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True})) + + client = OpenAI(base_url=base_url, workload_identity=workload_identity, admin_api_key=admin_api_key) + response = client.get( + "/organization/projects", + cast_to=httpx.Response, + options={"security": {"admin_api_key_auth": True}}, + ) + + assert response.request.headers.get("Authorization") == f"Bearer {admin_api_key}" + def test_workload_identity_is_mutually_exclusive_with_api_key(self) -> None: with pytest.raises( OpenAIError, @@ -1680,6 +1737,29 @@ async def test_validate_headers(self) -> None: ) ) + with update_env( + **{ + "OPENAI_API_KEY": Omit(), + "OPENAI_ADMIN_KEY": Omit(), + } + ): + no_credentials = AsyncOpenAI( + base_url=base_url, + api_key=None, + admin_api_key=None, + _enforce_credentials=False, + _strict_response_validation=True, + ) + lowercase_auth_request = no_credentials._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"authorization": "Bearer custom"}) + ) + assert lowercase_auth_request.headers.get("Authorization") == "Bearer custom" + + omitted_auth_request = no_credentials._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"authorization": Omit()}) + ) + assert "Authorization" not in omitted_auth_request.headers + with update_env( **{ "OPENAI_API_KEY": Omit(), @@ -1689,6 +1769,40 @@ async def test_validate_headers(self) -> None: with pytest.raises(OpenAIError, match="Missing credentials"): AsyncOpenAI(base_url=base_url, api_key=None, admin_api_key=None, _strict_response_validation=True) + @pytest.mark.respx(base_url=base_url) + async def test_api_key_provider_preserves_admin_auth(self, respx_mock: MockRouter) -> None: + respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True})) + + provider_called = False + + async def api_key_provider() -> str: + nonlocal provider_called + provider_called = True + return "dynamic-api-key" + + client = AsyncOpenAI(base_url=base_url, api_key=api_key_provider, admin_api_key=admin_api_key) + response = await client.get( + "/organization/projects", + cast_to=httpx.Response, + options={"security": {"admin_api_key_auth": True}}, + ) + + assert response.request.headers.get("Authorization") == f"Bearer {admin_api_key}" + assert provider_called is False + + @pytest.mark.respx(base_url=base_url) + async def test_workload_identity_preserves_admin_auth(self, respx_mock: MockRouter) -> None: + respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True})) + + client = AsyncOpenAI(base_url=base_url, workload_identity=workload_identity, admin_api_key=admin_api_key) + response = await client.get( + "/organization/projects", + cast_to=httpx.Response, + options={"security": {"admin_api_key_auth": True}}, + ) + + assert response.request.headers.get("Authorization") == f"Bearer {admin_api_key}" + async def test_default_query_option(self) -> None: client = AsyncOpenAI( base_url=base_url, From 9e4efd13bca6730d150cffba12151cc7c5f0e70a Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 30 Apr 2026 11:29:14 -0400 Subject: [PATCH 326/408] fix: avoid bearer fallback for admin auth --- src/openai/_client.py | 34 +++++++++++++++++----------------- tests/test_client.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/openai/_client.py b/src/openai/_client.py index f999aaab15..499a62dfe5 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -29,7 +29,7 @@ get_async_library, ) from ._compat import cached_property -from ._models import SecurityOptions +from ._models import SecurityOptions, FinalRequestOptions from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError @@ -408,14 +408,10 @@ def _send_with_auth_retry( **kwargs: Unpack[HttpxSendArgs], ) -> httpx.Response: used_workload_identity_auth = False - if self._api_key_provider is not None: - authorization = request.headers.get("Authorization") - if authorization is None or authorization == f"Bearer {self.api_key}": - request.headers["Authorization"] = f"Bearer {self._refresh_api_key()}" if self._workload_identity_auth is not None: authorization = request.headers.get("Authorization") - if authorization is None or authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": + if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": request.headers["Authorization"] = f"Bearer {self._workload_identity_auth.get_token()}" used_workload_identity_auth = True @@ -493,13 +489,17 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return - if self._api_key_provider is not None or self._workload_identity_auth is not None: - return - raise TypeError( '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) + @override + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if self._api_key_provider is not None and options.security.get("bearer_auth", False): + self._refresh_api_key() + + return super()._prepare_options(options) + def _refresh_api_key(self) -> str: if self._api_key_provider is not None: self.api_key = self._api_key_provider() @@ -914,14 +914,10 @@ async def _send_with_auth_retry( **kwargs: Unpack[HttpxSendArgs], ) -> httpx.Response: used_workload_identity_auth = False - if self._api_key_provider is not None: - authorization = request.headers.get("Authorization") - if authorization is None or authorization == f"Bearer {self.api_key}": - request.headers["Authorization"] = f"Bearer {await self._refresh_api_key()}" if self._workload_identity_auth is not None: authorization = request.headers.get("Authorization") - if authorization is None or authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": + if authorization == f"Bearer {WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}": request.headers["Authorization"] = f"Bearer {await self._workload_identity_auth.get_token_async()}" used_workload_identity_auth = True @@ -999,13 +995,17 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return - if self._api_key_provider is not None or self._workload_identity_auth is not None: - return - raise TypeError( '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) + @override + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if self._api_key_provider is not None and options.security.get("bearer_auth", False): + await self._refresh_api_key() + + return await super()._prepare_options(options) + async def _refresh_api_key(self) -> str: if self._api_key_provider is not None: self.api_key = await self._api_key_provider() diff --git a/tests/test_client.py b/tests/test_client.py index 99cd5c3b34..e2bb6ea966 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -545,6 +545,25 @@ def api_key_provider() -> str: assert response.request.headers.get("Authorization") == f"Bearer {admin_api_key}" assert provider_called is False + def test_api_key_provider_does_not_fill_admin_auth(self) -> None: + provider_called = False + + def api_key_provider() -> str: + nonlocal provider_called + provider_called = True + return "dynamic-api-key" + + with update_env(OPENAI_ADMIN_KEY=Omit()): + client = OpenAI(base_url=base_url, api_key=api_key_provider, admin_api_key=None) + with pytest.raises(TypeError, match="Could not resolve authentication method"): + client.get( + "/organization/projects", + cast_to=httpx.Response, + options={"security": {"admin_api_key_auth": True}}, + ) + + assert provider_called is False + @pytest.mark.respx(base_url=base_url) def test_workload_identity_preserves_admin_auth(self, respx_mock: MockRouter) -> None: respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True})) @@ -1790,6 +1809,25 @@ async def api_key_provider() -> str: assert response.request.headers.get("Authorization") == f"Bearer {admin_api_key}" assert provider_called is False + async def test_api_key_provider_does_not_fill_admin_auth(self) -> None: + provider_called = False + + async def api_key_provider() -> str: + nonlocal provider_called + provider_called = True + return "dynamic-api-key" + + with update_env(OPENAI_ADMIN_KEY=Omit()): + client = AsyncOpenAI(base_url=base_url, api_key=api_key_provider, admin_api_key=None) + with pytest.raises(TypeError, match="Could not resolve authentication method"): + await client.get( + "/organization/projects", + cast_to=httpx.Response, + options={"security": {"admin_api_key_auth": True}}, + ) + + assert provider_called is False + @pytest.mark.respx(base_url=base_url) async def test_workload_identity_preserves_admin_auth(self, respx_mock: MockRouter) -> None: respx_mock.get("/organization/projects").mock(return_value=httpx.Response(200, json={"ok": True})) From 15a9e05ada36a126f3ad9fab0eada8ad62b8389c Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Thu, 30 Apr 2026 11:46:31 -0400 Subject: [PATCH 327/408] fix: allow explicit Azure auth headers --- src/openai/lib/azure.py | 63 ++++++++++++++++++++++--- tests/lib/test_azure.py | 100 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 6 deletions(-) diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index e0b0956334..4fcae24788 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -8,11 +8,11 @@ import httpx from ..auth import WorkloadIdentity -from .._types import NOT_GIVEN, Omit, Query, Timeout, NotGiven +from .._types import NOT_GIVEN, Omit, Query, Headers, Timeout, NotGiven from .._utils import is_given, is_mapping from .._client import OpenAI, AsyncOpenAI from .._compat import model_copy -from .._models import FinalRequestOptions +from .._models import SecurityOptions, FinalRequestOptions from .._streaming import Stream, AsyncStream from .._exceptions import OpenAIError from .._base_client import DEFAULT_MAX_RETRIES, BaseClient @@ -43,6 +43,15 @@ API_KEY_SENTINEL = "".join(["<", "missing API key", ">"]) +def _has_header(headers: Headers, header: str) -> bool: + header = header.lower() + return any(key.lower() == header for key in headers) + + +def _has_auth_header(headers: Headers) -> bool: + return _has_header(headers, "Authorization") or _has_header(headers, "api-key") + + class MutuallyExclusiveAuthError(OpenAIError): def __init__(self) -> None: super().__init__( @@ -337,6 +346,25 @@ def _get_azure_ad_token(self) -> str | None: return None + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: # noqa: ARG002 + if self._azure_ad_token is not None: + return {"Authorization": f"Bearer {self._azure_ad_token}"} + + if self.api_key and self.api_key != API_KEY_SENTINEL: + return {"api-key": self.api_key} + + return {} + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if _has_auth_header(headers) or _has_auth_header(custom_headers): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key, azure_ad_token or azure_ad_token_provider to be set. Or for one of the `Authorization` or `api-key` headers to be explicitly supplied or omitted"' + ) + @override def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} @@ -346,11 +374,13 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: azure_ad_token = self._get_azure_ad_token() if azure_ad_token is not None: - if headers.get("Authorization") is None: + if not _has_header(headers, "Authorization"): headers["Authorization"] = f"Bearer {azure_ad_token}" elif self.api_key and self.api_key != API_KEY_SENTINEL: - if headers.get("api-key") is None: + if not _has_header(headers, "api-key"): headers["api-key"] = self.api_key + elif _has_auth_header(headers) or _has_auth_header(self.default_headers): + pass else: # should never be hit raise ValueError("Unable to handle auth") @@ -638,6 +668,25 @@ async def _get_azure_ad_token(self) -> str | None: return None + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: # noqa: ARG002 + if self._azure_ad_token is not None: + return {"Authorization": f"Bearer {self._azure_ad_token}"} + + if self.api_key and self.api_key != API_KEY_SENTINEL: + return {"api-key": self.api_key} + + return {} + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if _has_auth_header(headers) or _has_auth_header(custom_headers): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key, azure_ad_token or azure_ad_token_provider to be set. Or for one of the `Authorization` or `api-key` headers to be explicitly supplied or omitted"' + ) + @override async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} @@ -647,11 +696,13 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp azure_ad_token = await self._get_azure_ad_token() if azure_ad_token is not None: - if headers.get("Authorization") is None: + if not _has_header(headers, "Authorization"): headers["Authorization"] = f"Bearer {azure_ad_token}" elif self.api_key and self.api_key != API_KEY_SENTINEL: - if headers.get("api-key") is None: + if not _has_header(headers, "api-key"): headers["api-key"] = self.api_key + elif _has_auth_header(headers) or _has_auth_header(self.default_headers): + pass else: # should never be hit raise ValueError("Unable to handle auth") diff --git a/tests/lib/test_azure.py b/tests/lib/test_azure.py index 17ee885ed4..3e1d783e2c 100644 --- a/tests/lib/test_azure.py +++ b/tests/lib/test_azure.py @@ -91,6 +91,55 @@ def test_enforce_credentials_false_sync() -> None: ) +@pytest.mark.respx() +def test_enforce_credentials_false_sync_uses_default_api_key_header(respx_mock: MockRouter) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01" + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) + + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + client = AzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + default_headers={"api-key": "manual-api-key"}, + _enforce_credentials=False, + ) + client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers.get("api-key") == "manual-api-key" + assert calls[0].request.headers.get("Authorization") is None + + +@pytest.mark.respx() +def test_enforce_credentials_false_sync_uses_request_authorization_header(respx_mock: MockRouter) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01" + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) + + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + client = AzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + _enforce_credentials=False, + ) + client.chat.completions.create( + messages=[], + model="gpt-4", + extra_headers={"authorization": "Bearer manual-token"}, + ) + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers.get("Authorization") == "Bearer manual-token" + assert calls[0].request.headers.get("api-key") is None + + def test_enforce_credentials_true_sync() -> None: with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): with pytest.raises(OpenAIError, match="Missing credentials"): @@ -115,6 +164,57 @@ def test_enforce_credentials_false_async() -> None: ) +@pytest.mark.asyncio +@pytest.mark.respx() +async def test_enforce_credentials_false_async_uses_default_api_key_header(respx_mock: MockRouter) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01" + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) + + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + client = AsyncAzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + default_headers={"api-key": "manual-api-key"}, + _enforce_credentials=False, + ) + await client.chat.completions.create(messages=[], model="gpt-4") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers.get("api-key") == "manual-api-key" + assert calls[0].request.headers.get("Authorization") is None + + +@pytest.mark.asyncio +@pytest.mark.respx() +async def test_enforce_credentials_false_async_uses_request_authorization_header(respx_mock: MockRouter) -> None: + respx_mock.post( + "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01" + ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"})) + + with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): + client = AsyncAzureOpenAI( + api_version="2024-02-01", + api_key=None, + azure_ad_token=None, + azure_ad_token_provider=None, + azure_endpoint="https://example-resource.azure.openai.com", + _enforce_credentials=False, + ) + await client.chat.completions.create( + messages=[], + model="gpt-4", + extra_headers={"authorization": "Bearer manual-token"}, + ) + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers.get("Authorization") == "Bearer manual-token" + assert calls[0].request.headers.get("api-key") is None + + def test_enforce_credentials_true_async() -> None: with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()): with pytest.raises(OpenAIError, match="Missing credentials"): From 42b2b60af44566ce3d1d6d37f2bb9d77274e65dd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 17:32:07 +0000 Subject: [PATCH 328/408] fix(types): correct created_at and completed_at to float in Response --- .stats.yml | 4 ++-- src/openai/types/responses/response.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 73d7722354..fc59bc229a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 153 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-7712c92246b94f811f6e3d33569242e75b22497d51df8cc27b16a6a205b4560a.yml -openapi_spec_hash: d431cf9d2bf2b01122bb98cc7b7a357b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-39747efaffb2331dfdeb20a8cb4293abaf871f7b6545f8d7230baf7843bc4d09.yml +openapi_spec_hash: 142dc3e8e2e0a4f1017102e1dd49a85b config_hash: 88be7d18ee7f33affcdad19572cd0c91 diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index bfcf2460d6..0d2491ea7c 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -60,7 +60,7 @@ class Response(BaseModel): id: str """Unique identifier for this Response.""" - created_at: int + created_at: float """Unix timestamp (in seconds) of when this Response was created.""" error: Optional[ResponseError] = None @@ -165,7 +165,7 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/background). """ - completed_at: Optional[int] = None + completed_at: Optional[float] = None """ Unix timestamp (in seconds) of when this Response was completed. Only present when the status is `completed`. From d05f3a875fc78519711067350605d6226aed33b8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 18:00:45 +0000 Subject: [PATCH 329/408] feat(api): manual updates Include all admin APIs in the code generation. --- .stats.yml | 6 +- api.md | 324 ++++ .../resources/admin/organization/__init__.py | 112 ++ .../admin/organization/admin_api_keys.py | 468 +++++ .../admin/organization/certificates.py | 815 ++++++++ .../admin/organization/groups/__init__.py | 47 + .../admin/organization/groups/groups.py | 544 ++++++ .../admin/organization/groups/roles.py | 398 ++++ .../admin/organization/groups/users.py | 400 ++++ .../resources/admin/organization/invites.py | 500 +++++ .../admin/organization/organization.py | 256 +++ .../admin/organization/projects/__init__.py | 117 ++ .../admin/organization/projects/api_keys.py | 405 ++++ .../organization/projects/certificates.py | 426 ++++ .../organization/projects/groups/__init__.py | 33 + .../organization/projects/groups/groups.py | 451 +++++ .../organization/projects/groups/roles.py | 426 ++++ .../admin/organization/projects/projects.py | 824 ++++++++ .../organization/projects/rate_limits.py | 379 ++++ .../admin/organization/projects/roles.py | 552 ++++++ .../organization/projects/service_accounts.py | 512 +++++ .../organization/projects/users/__init__.py | 33 + .../organization/projects/users/roles.py | 426 ++++ .../organization/projects/users/users.py | 659 +++++++ .../resources/admin/organization/roles.py | 526 +++++ .../resources/admin/organization/usage.py | 1724 +++++++++++++++++ .../admin/organization/users/__init__.py | 33 + .../admin/organization/users/roles.py | 398 ++++ .../admin/organization/users/users.py | 509 +++++ .../types/admin/organization/__init__.py | 57 + .../types/admin/organization/admin_api_key.py | 54 + .../admin_api_key_create_params.py | 11 + .../admin_api_key_delete_response.py | 15 + .../organization/admin_api_key_list_params.py | 19 + .../types/admin/organization/certificate.py | 51 + .../certificate_activate_params.py | 13 + .../organization/certificate_create_params.py | 15 + .../certificate_deactivate_params.py | 13 + .../certificate_delete_response.py | 15 + .../organization/certificate_list_params.py | 30 + .../certificate_retrieve_params.py | 17 + .../organization/certificate_update_params.py | 12 + src/openai/types/admin/organization/group.py | 24 + .../admin/organization/group_create_params.py | 12 + .../organization/group_delete_response.py | 20 + .../admin/organization/group_list_params.py | 27 + .../admin/organization/group_update_params.py | 12 + .../organization/group_update_response.py | 24 + .../admin/organization/groups/__init__.py | 13 + .../organization/groups/role_create_params.py | 12 + .../groups/role_create_response.py | 40 + .../groups/role_delete_response.py | 18 + .../organization/groups/role_list_params.py | 22 + .../organization/groups/role_list_response.py | 46 + .../organization/groups/user_create_params.py | 12 + .../groups/user_create_response.py | 20 + .../groups/user_delete_response.py | 17 + .../organization/groups/user_list_params.py | 25 + src/openai/types/admin/organization/invite.py | 47 + .../organization/invite_create_params.py | 31 + .../organization/invite_delete_response.py | 16 + .../admin/organization/invite_list_params.py | 24 + .../admin/organization/organization_user.py | 29 + .../types/admin/organization/project.py | 30 + .../organization/project_create_params.py | 21 + .../admin/organization/project_list_params.py | 30 + .../organization/project_update_params.py | 12 + .../admin/organization/projects/__init__.py | 31 + .../projects/api_key_delete_response.py | 15 + .../projects/api_key_list_params.py | 24 + .../projects/certificate_activate_params.py | 13 + .../projects/certificate_deactivate_params.py | 13 + .../projects/certificate_list_params.py | 30 + .../projects/group_create_params.py | 15 + .../projects/group_delete_response.py | 17 + .../projects/group_list_params.py | 22 + .../organization/projects/groups/__init__.py | 9 + .../projects/groups/role_create_params.py | 14 + .../projects/groups/role_create_response.py | 40 + .../projects/groups/role_delete_response.py | 18 + .../projects/groups/role_list_params.py | 24 + .../projects/groups/role_list_response.py | 46 + .../organization/projects/project_api_key.py | 45 + .../organization/projects/project_group.py | 26 + .../projects/project_rate_limit.py | 39 + .../projects/project_service_account.py | 26 + .../organization/projects/project_user.py | 29 + .../rate_limit_list_rate_limits_params.py | 30 + .../rate_limit_update_rate_limit_params.py | 29 + .../projects/role_create_params.py | 21 + .../projects/role_delete_response.py | 20 + .../organization/projects/role_list_params.py | 22 + .../projects/role_update_params.py | 23 + .../projects/service_account_create_params.py | 12 + .../service_account_create_response.py | 35 + .../service_account_delete_response.py | 15 + .../projects/service_account_list_params.py | 24 + .../projects/user_create_params.py | 15 + .../projects/user_delete_response.py | 15 + .../organization/projects/user_list_params.py | 24 + .../projects/user_update_params.py | 14 + .../organization/projects/users/__init__.py | 9 + .../projects/users/role_create_params.py | 14 + .../projects/users/role_create_response.py | 22 + .../projects/users/role_delete_response.py | 18 + .../projects/users/role_list_params.py | 24 + .../projects/users/role_list_response.py | 46 + src/openai/types/admin/organization/role.py | 36 + .../admin/organization/role_create_params.py | 21 + .../organization/role_delete_response.py | 20 + .../admin/organization/role_list_params.py | 22 + .../admin/organization/role_update_params.py | 21 + .../usage_audio_speeches_params.py | 57 + .../usage_audio_speeches_response.py | 390 ++++ .../usage_audio_transcriptions_params.py | 57 + .../usage_audio_transcriptions_response.py | 390 ++++ .../usage_code_interpreter_sessions_params.py | 47 + ...sage_code_interpreter_sessions_response.py | 390 ++++ .../organization/usage_completions_params.py | 63 + .../usage_completions_response.py | 390 ++++ .../admin/organization/usage_costs_params.py | 49 + .../organization/usage_costs_response.py | 390 ++++ .../organization/usage_embeddings_params.py | 57 + .../organization/usage_embeddings_response.py | 390 ++++ .../admin/organization/usage_images_params.py | 71 + .../organization/usage_images_response.py | 390 ++++ .../organization/usage_moderations_params.py | 57 + .../usage_moderations_response.py | 390 ++++ .../usage_vector_stores_params.py | 47 + .../usage_vector_stores_response.py | 390 ++++ .../organization/user_delete_response.py | 15 + .../admin/organization/user_list_params.py | 29 + .../admin/organization/user_update_params.py | 12 + .../admin/organization/users/__init__.py | 9 + .../organization/users/role_create_params.py | 12 + .../users/role_create_response.py | 22 + .../users/role_delete_response.py | 18 + .../organization/users/role_list_params.py | 22 + .../organization/users/role_list_response.py | 46 + .../admin/organization/groups/__init__.py | 1 + .../admin/organization/groups/test_roles.py | 305 +++ .../admin/organization/groups/test_users.py | 305 +++ .../admin/organization/projects/__init__.py | 1 + .../organization/projects/groups/__init__.py | 1 + .../projects/groups/test_roles.py | 373 ++++ .../organization/projects/test_api_keys.py | 311 +++ .../projects/test_certificates.py | 289 +++ .../organization/projects/test_groups.py | 312 +++ .../organization/projects/test_rate_limits.py | 247 +++ .../admin/organization/projects/test_roles.py | 450 +++++ .../projects/test_service_accounts.py | 399 ++++ .../admin/organization/projects/test_users.py | 512 +++++ .../organization/projects/users/__init__.py | 1 + .../organization/projects/users/test_roles.py | 373 ++++ .../admin/organization/test_admin_api_keys.py | 310 +++ .../admin/organization/test_certificates.py | 550 ++++++ .../admin/organization/test_groups.py | 319 +++ .../admin/organization/test_invites.py | 339 ++++ .../admin/organization/test_projects.py | 407 ++++ .../admin/organization/test_roles.py | 354 ++++ .../admin/organization/test_usage.py | 870 +++++++++ .../admin/organization/test_users.py | 329 ++++ .../admin/organization/users/__init__.py | 1 + .../admin/organization/users/test_roles.py | 305 +++ 164 files changed, 26118 insertions(+), 3 deletions(-) create mode 100644 src/openai/resources/admin/organization/admin_api_keys.py create mode 100644 src/openai/resources/admin/organization/certificates.py create mode 100644 src/openai/resources/admin/organization/groups/__init__.py create mode 100644 src/openai/resources/admin/organization/groups/groups.py create mode 100644 src/openai/resources/admin/organization/groups/roles.py create mode 100644 src/openai/resources/admin/organization/groups/users.py create mode 100644 src/openai/resources/admin/organization/invites.py create mode 100644 src/openai/resources/admin/organization/projects/__init__.py create mode 100644 src/openai/resources/admin/organization/projects/api_keys.py create mode 100644 src/openai/resources/admin/organization/projects/certificates.py create mode 100644 src/openai/resources/admin/organization/projects/groups/__init__.py create mode 100644 src/openai/resources/admin/organization/projects/groups/groups.py create mode 100644 src/openai/resources/admin/organization/projects/groups/roles.py create mode 100644 src/openai/resources/admin/organization/projects/projects.py create mode 100644 src/openai/resources/admin/organization/projects/rate_limits.py create mode 100644 src/openai/resources/admin/organization/projects/roles.py create mode 100644 src/openai/resources/admin/organization/projects/service_accounts.py create mode 100644 src/openai/resources/admin/organization/projects/users/__init__.py create mode 100644 src/openai/resources/admin/organization/projects/users/roles.py create mode 100644 src/openai/resources/admin/organization/projects/users/users.py create mode 100644 src/openai/resources/admin/organization/roles.py create mode 100644 src/openai/resources/admin/organization/usage.py create mode 100644 src/openai/resources/admin/organization/users/__init__.py create mode 100644 src/openai/resources/admin/organization/users/roles.py create mode 100644 src/openai/resources/admin/organization/users/users.py create mode 100644 src/openai/types/admin/organization/admin_api_key.py create mode 100644 src/openai/types/admin/organization/admin_api_key_create_params.py create mode 100644 src/openai/types/admin/organization/admin_api_key_delete_response.py create mode 100644 src/openai/types/admin/organization/admin_api_key_list_params.py create mode 100644 src/openai/types/admin/organization/certificate.py create mode 100644 src/openai/types/admin/organization/certificate_activate_params.py create mode 100644 src/openai/types/admin/organization/certificate_create_params.py create mode 100644 src/openai/types/admin/organization/certificate_deactivate_params.py create mode 100644 src/openai/types/admin/organization/certificate_delete_response.py create mode 100644 src/openai/types/admin/organization/certificate_list_params.py create mode 100644 src/openai/types/admin/organization/certificate_retrieve_params.py create mode 100644 src/openai/types/admin/organization/certificate_update_params.py create mode 100644 src/openai/types/admin/organization/group.py create mode 100644 src/openai/types/admin/organization/group_create_params.py create mode 100644 src/openai/types/admin/organization/group_delete_response.py create mode 100644 src/openai/types/admin/organization/group_list_params.py create mode 100644 src/openai/types/admin/organization/group_update_params.py create mode 100644 src/openai/types/admin/organization/group_update_response.py create mode 100644 src/openai/types/admin/organization/groups/__init__.py create mode 100644 src/openai/types/admin/organization/groups/role_create_params.py create mode 100644 src/openai/types/admin/organization/groups/role_create_response.py create mode 100644 src/openai/types/admin/organization/groups/role_delete_response.py create mode 100644 src/openai/types/admin/organization/groups/role_list_params.py create mode 100644 src/openai/types/admin/organization/groups/role_list_response.py create mode 100644 src/openai/types/admin/organization/groups/user_create_params.py create mode 100644 src/openai/types/admin/organization/groups/user_create_response.py create mode 100644 src/openai/types/admin/organization/groups/user_delete_response.py create mode 100644 src/openai/types/admin/organization/groups/user_list_params.py create mode 100644 src/openai/types/admin/organization/invite.py create mode 100644 src/openai/types/admin/organization/invite_create_params.py create mode 100644 src/openai/types/admin/organization/invite_delete_response.py create mode 100644 src/openai/types/admin/organization/invite_list_params.py create mode 100644 src/openai/types/admin/organization/organization_user.py create mode 100644 src/openai/types/admin/organization/project.py create mode 100644 src/openai/types/admin/organization/project_create_params.py create mode 100644 src/openai/types/admin/organization/project_list_params.py create mode 100644 src/openai/types/admin/organization/project_update_params.py create mode 100644 src/openai/types/admin/organization/projects/__init__.py create mode 100644 src/openai/types/admin/organization/projects/api_key_delete_response.py create mode 100644 src/openai/types/admin/organization/projects/api_key_list_params.py create mode 100644 src/openai/types/admin/organization/projects/certificate_activate_params.py create mode 100644 src/openai/types/admin/organization/projects/certificate_deactivate_params.py create mode 100644 src/openai/types/admin/organization/projects/certificate_list_params.py create mode 100644 src/openai/types/admin/organization/projects/group_create_params.py create mode 100644 src/openai/types/admin/organization/projects/group_delete_response.py create mode 100644 src/openai/types/admin/organization/projects/group_list_params.py create mode 100644 src/openai/types/admin/organization/projects/groups/__init__.py create mode 100644 src/openai/types/admin/organization/projects/groups/role_create_params.py create mode 100644 src/openai/types/admin/organization/projects/groups/role_create_response.py create mode 100644 src/openai/types/admin/organization/projects/groups/role_delete_response.py create mode 100644 src/openai/types/admin/organization/projects/groups/role_list_params.py create mode 100644 src/openai/types/admin/organization/projects/groups/role_list_response.py create mode 100644 src/openai/types/admin/organization/projects/project_api_key.py create mode 100644 src/openai/types/admin/organization/projects/project_group.py create mode 100644 src/openai/types/admin/organization/projects/project_rate_limit.py create mode 100644 src/openai/types/admin/organization/projects/project_service_account.py create mode 100644 src/openai/types/admin/organization/projects/project_user.py create mode 100644 src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py create mode 100644 src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py create mode 100644 src/openai/types/admin/organization/projects/role_create_params.py create mode 100644 src/openai/types/admin/organization/projects/role_delete_response.py create mode 100644 src/openai/types/admin/organization/projects/role_list_params.py create mode 100644 src/openai/types/admin/organization/projects/role_update_params.py create mode 100644 src/openai/types/admin/organization/projects/service_account_create_params.py create mode 100644 src/openai/types/admin/organization/projects/service_account_create_response.py create mode 100644 src/openai/types/admin/organization/projects/service_account_delete_response.py create mode 100644 src/openai/types/admin/organization/projects/service_account_list_params.py create mode 100644 src/openai/types/admin/organization/projects/user_create_params.py create mode 100644 src/openai/types/admin/organization/projects/user_delete_response.py create mode 100644 src/openai/types/admin/organization/projects/user_list_params.py create mode 100644 src/openai/types/admin/organization/projects/user_update_params.py create mode 100644 src/openai/types/admin/organization/projects/users/__init__.py create mode 100644 src/openai/types/admin/organization/projects/users/role_create_params.py create mode 100644 src/openai/types/admin/organization/projects/users/role_create_response.py create mode 100644 src/openai/types/admin/organization/projects/users/role_delete_response.py create mode 100644 src/openai/types/admin/organization/projects/users/role_list_params.py create mode 100644 src/openai/types/admin/organization/projects/users/role_list_response.py create mode 100644 src/openai/types/admin/organization/role.py create mode 100644 src/openai/types/admin/organization/role_create_params.py create mode 100644 src/openai/types/admin/organization/role_delete_response.py create mode 100644 src/openai/types/admin/organization/role_list_params.py create mode 100644 src/openai/types/admin/organization/role_update_params.py create mode 100644 src/openai/types/admin/organization/usage_audio_speeches_params.py create mode 100644 src/openai/types/admin/organization/usage_audio_speeches_response.py create mode 100644 src/openai/types/admin/organization/usage_audio_transcriptions_params.py create mode 100644 src/openai/types/admin/organization/usage_audio_transcriptions_response.py create mode 100644 src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py create mode 100644 src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py create mode 100644 src/openai/types/admin/organization/usage_completions_params.py create mode 100644 src/openai/types/admin/organization/usage_completions_response.py create mode 100644 src/openai/types/admin/organization/usage_costs_params.py create mode 100644 src/openai/types/admin/organization/usage_costs_response.py create mode 100644 src/openai/types/admin/organization/usage_embeddings_params.py create mode 100644 src/openai/types/admin/organization/usage_embeddings_response.py create mode 100644 src/openai/types/admin/organization/usage_images_params.py create mode 100644 src/openai/types/admin/organization/usage_images_response.py create mode 100644 src/openai/types/admin/organization/usage_moderations_params.py create mode 100644 src/openai/types/admin/organization/usage_moderations_response.py create mode 100644 src/openai/types/admin/organization/usage_vector_stores_params.py create mode 100644 src/openai/types/admin/organization/usage_vector_stores_response.py create mode 100644 src/openai/types/admin/organization/user_delete_response.py create mode 100644 src/openai/types/admin/organization/user_list_params.py create mode 100644 src/openai/types/admin/organization/user_update_params.py create mode 100644 src/openai/types/admin/organization/users/__init__.py create mode 100644 src/openai/types/admin/organization/users/role_create_params.py create mode 100644 src/openai/types/admin/organization/users/role_create_response.py create mode 100644 src/openai/types/admin/organization/users/role_delete_response.py create mode 100644 src/openai/types/admin/organization/users/role_list_params.py create mode 100644 src/openai/types/admin/organization/users/role_list_response.py create mode 100644 tests/api_resources/admin/organization/groups/__init__.py create mode 100644 tests/api_resources/admin/organization/groups/test_roles.py create mode 100644 tests/api_resources/admin/organization/groups/test_users.py create mode 100644 tests/api_resources/admin/organization/projects/__init__.py create mode 100644 tests/api_resources/admin/organization/projects/groups/__init__.py create mode 100644 tests/api_resources/admin/organization/projects/groups/test_roles.py create mode 100644 tests/api_resources/admin/organization/projects/test_api_keys.py create mode 100644 tests/api_resources/admin/organization/projects/test_certificates.py create mode 100644 tests/api_resources/admin/organization/projects/test_groups.py create mode 100644 tests/api_resources/admin/organization/projects/test_rate_limits.py create mode 100644 tests/api_resources/admin/organization/projects/test_roles.py create mode 100644 tests/api_resources/admin/organization/projects/test_service_accounts.py create mode 100644 tests/api_resources/admin/organization/projects/test_users.py create mode 100644 tests/api_resources/admin/organization/projects/users/__init__.py create mode 100644 tests/api_resources/admin/organization/projects/users/test_roles.py create mode 100644 tests/api_resources/admin/organization/test_admin_api_keys.py create mode 100644 tests/api_resources/admin/organization/test_certificates.py create mode 100644 tests/api_resources/admin/organization/test_groups.py create mode 100644 tests/api_resources/admin/organization/test_invites.py create mode 100644 tests/api_resources/admin/organization/test_projects.py create mode 100644 tests/api_resources/admin/organization/test_roles.py create mode 100644 tests/api_resources/admin/organization/test_usage.py create mode 100644 tests/api_resources/admin/organization/test_users.py create mode 100644 tests/api_resources/admin/organization/users/__init__.py create mode 100644 tests/api_resources/admin/organization/users/test_roles.py diff --git a/.stats.yml b/.stats.yml index fc59bc229a..7b55dfd466 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 153 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-39747efaffb2331dfdeb20a8cb4293abaf871f7b6545f8d7230baf7843bc4d09.yml +configured_endpoints: 233 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5b00c10325d1747a1b7da6289fe47e18f3d69503925bd3b6c1c67bbadc5a7f8f.yml openapi_spec_hash: 142dc3e8e2e0a4f1017102e1dd49a85b -config_hash: 88be7d18ee7f33affcdad19572cd0c91 +config_hash: 53256195d26013f6fe5ee634fb1c92f6 diff --git a/api.md b/api.md index c1378fa60f..f80a5b12c8 100644 --- a/api.md +++ b/api.md @@ -723,6 +723,330 @@ Methods: - client.admin.organization.audit_logs.list(\*\*params) -> SyncConversationCursorPage[AuditLogListResponse] +### AdminAPIKeys + +Types: + +```python +from openai.types.admin.organization import AdminAPIKey, AdminAPIKeyDeleteResponse +``` + +Methods: + +- client.admin.organization.admin_api_keys.create(\*\*params) -> AdminAPIKey +- client.admin.organization.admin_api_keys.retrieve(key_id) -> AdminAPIKey +- client.admin.organization.admin_api_keys.list(\*\*params) -> SyncCursorPage[AdminAPIKey] +- client.admin.organization.admin_api_keys.delete(key_id) -> AdminAPIKeyDeleteResponse + +### Usage + +Types: + +```python +from openai.types.admin.organization import ( + UsageAudioSpeechesResponse, + UsageAudioTranscriptionsResponse, + UsageCodeInterpreterSessionsResponse, + UsageCompletionsResponse, + UsageCostsResponse, + UsageEmbeddingsResponse, + UsageImagesResponse, + UsageModerationsResponse, + UsageVectorStoresResponse, +) +``` + +Methods: + +- client.admin.organization.usage.audio_speeches(\*\*params) -> UsageAudioSpeechesResponse +- client.admin.organization.usage.audio_transcriptions(\*\*params) -> UsageAudioTranscriptionsResponse +- client.admin.organization.usage.code_interpreter_sessions(\*\*params) -> UsageCodeInterpreterSessionsResponse +- client.admin.organization.usage.completions(\*\*params) -> UsageCompletionsResponse +- client.admin.organization.usage.costs(\*\*params) -> UsageCostsResponse +- client.admin.organization.usage.embeddings(\*\*params) -> UsageEmbeddingsResponse +- client.admin.organization.usage.images(\*\*params) -> UsageImagesResponse +- client.admin.organization.usage.moderations(\*\*params) -> UsageModerationsResponse +- client.admin.organization.usage.vector_stores(\*\*params) -> UsageVectorStoresResponse + +### Invites + +Types: + +```python +from openai.types.admin.organization import Invite, InviteDeleteResponse +``` + +Methods: + +- client.admin.organization.invites.create(\*\*params) -> Invite +- client.admin.organization.invites.retrieve(invite_id) -> Invite +- client.admin.organization.invites.list(\*\*params) -> SyncConversationCursorPage[Invite] +- client.admin.organization.invites.delete(invite_id) -> InviteDeleteResponse + +### Users + +Types: + +```python +from openai.types.admin.organization import OrganizationUser, UserDeleteResponse +``` + +Methods: + +- client.admin.organization.users.retrieve(user_id) -> OrganizationUser +- client.admin.organization.users.update(user_id, \*\*params) -> OrganizationUser +- client.admin.organization.users.list(\*\*params) -> SyncConversationCursorPage[OrganizationUser] +- client.admin.organization.users.delete(user_id) -> UserDeleteResponse + +#### Roles + +Types: + +```python +from openai.types.admin.organization.users import ( + RoleCreateResponse, + RoleListResponse, + RoleDeleteResponse, +) +``` + +Methods: + +- client.admin.organization.users.roles.create(user_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.users.roles.list(user_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.users.roles.delete(role_id, \*, user_id) -> RoleDeleteResponse + +### Groups + +Types: + +```python +from openai.types.admin.organization import Group, GroupUpdateResponse, GroupDeleteResponse +``` + +Methods: + +- client.admin.organization.groups.create(\*\*params) -> Group +- client.admin.organization.groups.update(group_id, \*\*params) -> GroupUpdateResponse +- client.admin.organization.groups.list(\*\*params) -> SyncCursorPage[Group] +- client.admin.organization.groups.delete(group_id) -> GroupDeleteResponse + +#### Users + +Types: + +```python +from openai.types.admin.organization.groups import UserCreateResponse, UserDeleteResponse +``` + +Methods: + +- client.admin.organization.groups.users.create(group_id, \*\*params) -> UserCreateResponse +- client.admin.organization.groups.users.list(group_id, \*\*params) -> SyncCursorPage[OrganizationUser] +- client.admin.organization.groups.users.delete(user_id, \*, group_id) -> UserDeleteResponse + +#### Roles + +Types: + +```python +from openai.types.admin.organization.groups import ( + RoleCreateResponse, + RoleListResponse, + RoleDeleteResponse, +) +``` + +Methods: + +- client.admin.organization.groups.roles.create(group_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.groups.roles.list(group_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.groups.roles.delete(role_id, \*, group_id) -> RoleDeleteResponse + +### Roles + +Types: + +```python +from openai.types.admin.organization import Role, RoleDeleteResponse +``` + +Methods: + +- client.admin.organization.roles.create(\*\*params) -> Role +- client.admin.organization.roles.update(role_id, \*\*params) -> Role +- client.admin.organization.roles.list(\*\*params) -> SyncCursorPage[Role] +- client.admin.organization.roles.delete(role_id) -> RoleDeleteResponse + +### Certificates + +Types: + +```python +from openai.types.admin.organization import Certificate, CertificateDeleteResponse +``` + +Methods: + +- client.admin.organization.certificates.create(\*\*params) -> Certificate +- client.admin.organization.certificates.retrieve(certificate_id, \*\*params) -> Certificate +- client.admin.organization.certificates.update(certificate_id, \*\*params) -> Certificate +- client.admin.organization.certificates.list(\*\*params) -> SyncConversationCursorPage[Certificate] +- client.admin.organization.certificates.delete(certificate_id) -> CertificateDeleteResponse +- client.admin.organization.certificates.activate(\*\*params) -> SyncPage[Certificate] +- client.admin.organization.certificates.deactivate(\*\*params) -> SyncPage[Certificate] + +### Projects + +Types: + +```python +from openai.types.admin.organization import Project +``` + +Methods: + +- client.admin.organization.projects.create(\*\*params) -> Project +- client.admin.organization.projects.retrieve(project_id) -> Project +- client.admin.organization.projects.update(project_id, \*\*params) -> Project +- client.admin.organization.projects.list(\*\*params) -> SyncConversationCursorPage[Project] +- client.admin.organization.projects.archive(project_id) -> Project + +#### Users + +Types: + +```python +from openai.types.admin.organization.projects import ProjectUser, UserDeleteResponse +``` + +Methods: + +- client.admin.organization.projects.users.create(project_id, \*\*params) -> ProjectUser +- client.admin.organization.projects.users.retrieve(user_id, \*, project_id) -> ProjectUser +- client.admin.organization.projects.users.update(user_id, \*, project_id, \*\*params) -> ProjectUser +- client.admin.organization.projects.users.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectUser] +- client.admin.organization.projects.users.delete(user_id, \*, project_id) -> UserDeleteResponse + +##### Roles + +Types: + +```python +from openai.types.admin.organization.projects.users import ( + RoleCreateResponse, + RoleListResponse, + RoleDeleteResponse, +) +``` + +Methods: + +- client.admin.organization.projects.users.roles.create(user_id, \*, project_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.projects.users.roles.list(user_id, \*, project_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.projects.users.roles.delete(role_id, \*, project_id, user_id) -> RoleDeleteResponse + +#### ServiceAccounts + +Types: + +```python +from openai.types.admin.organization.projects import ( + ProjectServiceAccount, + ServiceAccountCreateResponse, + ServiceAccountDeleteResponse, +) +``` + +Methods: + +- client.admin.organization.projects.service_accounts.create(project_id, \*\*params) -> ServiceAccountCreateResponse +- client.admin.organization.projects.service_accounts.retrieve(service_account_id, \*, project_id) -> ProjectServiceAccount +- client.admin.organization.projects.service_accounts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectServiceAccount] +- client.admin.organization.projects.service_accounts.delete(service_account_id, \*, project_id) -> ServiceAccountDeleteResponse + +#### APIKeys + +Types: + +```python +from openai.types.admin.organization.projects import ProjectAPIKey, APIKeyDeleteResponse +``` + +Methods: + +- client.admin.organization.projects.api_keys.retrieve(key_id, \*, project_id) -> ProjectAPIKey +- client.admin.organization.projects.api_keys.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectAPIKey] +- client.admin.organization.projects.api_keys.delete(key_id, \*, project_id) -> APIKeyDeleteResponse + +#### RateLimits + +Types: + +```python +from openai.types.admin.organization.projects import ProjectRateLimit +``` + +Methods: + +- client.admin.organization.projects.rate_limits.list_rate_limits(project_id, \*\*params) -> SyncConversationCursorPage[ProjectRateLimit] +- client.admin.organization.projects.rate_limits.update_rate_limit(rate_limit_id, \*, project_id, \*\*params) -> ProjectRateLimit + +#### Groups + +Types: + +```python +from openai.types.admin.organization.projects import ProjectGroup, GroupDeleteResponse +``` + +Methods: + +- client.admin.organization.projects.groups.create(project_id, \*\*params) -> ProjectGroup +- client.admin.organization.projects.groups.list(project_id, \*\*params) -> SyncCursorPage[ProjectGroup] +- client.admin.organization.projects.groups.delete(group_id, \*, project_id) -> GroupDeleteResponse + +##### Roles + +Types: + +```python +from openai.types.admin.organization.projects.groups import ( + RoleCreateResponse, + RoleListResponse, + RoleDeleteResponse, +) +``` + +Methods: + +- client.admin.organization.projects.groups.roles.create(group_id, \*, project_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.projects.groups.roles.list(group_id, \*, project_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.projects.groups.roles.delete(role_id, \*, project_id, group_id) -> RoleDeleteResponse + +#### Roles + +Types: + +```python +from openai.types.admin.organization.projects import RoleDeleteResponse +``` + +Methods: + +- client.admin.organization.projects.roles.create(project_id, \*\*params) -> Role +- client.admin.organization.projects.roles.update(role_id, \*, project_id, \*\*params) -> Role +- client.admin.organization.projects.roles.list(project_id, \*\*params) -> SyncCursorPage[Role] +- client.admin.organization.projects.roles.delete(role_id, \*, project_id) -> RoleDeleteResponse + +#### Certificates + +Methods: + +- client.admin.organization.projects.certificates.list(project_id, \*\*params) -> SyncConversationCursorPage[Certificate] +- client.admin.organization.projects.certificates.activate(project_id, \*\*params) -> SyncPage[Certificate] +- client.admin.organization.projects.certificates.deactivate(project_id, \*\*params) -> SyncPage[Certificate] + # [Responses](src/openai/resources/responses/api.md) # [Realtime](src/openai/resources/realtime/api.md) diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py index d3e8314a44..50641088bb 100644 --- a/src/openai/resources/admin/organization/__init__.py +++ b/src/openai/resources/admin/organization/__init__.py @@ -1,5 +1,53 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .usage import ( + Usage, + AsyncUsage, + UsageWithRawResponse, + AsyncUsageWithRawResponse, + UsageWithStreamingResponse, + AsyncUsageWithStreamingResponse, +) +from .users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from .groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) +from .invites import ( + Invites, + AsyncInvites, + InvitesWithRawResponse, + AsyncInvitesWithRawResponse, + InvitesWithStreamingResponse, + AsyncInvitesWithStreamingResponse, +) +from .projects import ( + Projects, + AsyncProjects, + ProjectsWithRawResponse, + AsyncProjectsWithRawResponse, + ProjectsWithStreamingResponse, + AsyncProjectsWithStreamingResponse, +) from .audit_logs import ( AuditLogs, AsyncAuditLogs, @@ -8,6 +56,14 @@ AuditLogsWithStreamingResponse, AsyncAuditLogsWithStreamingResponse, ) +from .certificates import ( + Certificates, + AsyncCertificates, + CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse, +) from .organization import ( Organization, AsyncOrganization, @@ -16,6 +72,14 @@ OrganizationWithStreamingResponse, AsyncOrganizationWithStreamingResponse, ) +from .admin_api_keys import ( + AdminAPIKeys, + AsyncAdminAPIKeys, + AdminAPIKeysWithRawResponse, + AsyncAdminAPIKeysWithRawResponse, + AdminAPIKeysWithStreamingResponse, + AsyncAdminAPIKeysWithStreamingResponse, +) __all__ = [ "AuditLogs", @@ -24,6 +88,54 @@ "AsyncAuditLogsWithRawResponse", "AuditLogsWithStreamingResponse", "AsyncAuditLogsWithStreamingResponse", + "AdminAPIKeys", + "AsyncAdminAPIKeys", + "AdminAPIKeysWithRawResponse", + "AsyncAdminAPIKeysWithRawResponse", + "AdminAPIKeysWithStreamingResponse", + "AsyncAdminAPIKeysWithStreamingResponse", + "Usage", + "AsyncUsage", + "UsageWithRawResponse", + "AsyncUsageWithRawResponse", + "UsageWithStreamingResponse", + "AsyncUsageWithStreamingResponse", + "Invites", + "AsyncInvites", + "InvitesWithRawResponse", + "AsyncInvitesWithRawResponse", + "InvitesWithStreamingResponse", + "AsyncInvitesWithStreamingResponse", + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Certificates", + "AsyncCertificates", + "CertificatesWithRawResponse", + "AsyncCertificatesWithRawResponse", + "CertificatesWithStreamingResponse", + "AsyncCertificatesWithStreamingResponse", + "Projects", + "AsyncProjects", + "ProjectsWithRawResponse", + "AsyncProjectsWithRawResponse", + "ProjectsWithStreamingResponse", + "AsyncProjectsWithStreamingResponse", "Organization", "AsyncOrganization", "OrganizationWithRawResponse", diff --git a/src/openai/resources/admin/organization/admin_api_keys.py b/src/openai/resources/admin/organization/admin_api_keys.py new file mode 100644 index 0000000000..bf3ff7abd0 --- /dev/null +++ b/src/openai/resources/admin/organization/admin_api_keys.py @@ -0,0 +1,468 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import admin_api_key_list_params, admin_api_key_create_params +from ....types.admin.organization.admin_api_key import AdminAPIKey +from ....types.admin.organization.admin_api_key_delete_response import AdminAPIKeyDeleteResponse + +__all__ = ["AdminAPIKeys", "AsyncAdminAPIKeys"] + + +class AdminAPIKeys(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AdminAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AdminAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AdminAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AdminAPIKeysWithStreamingResponse(self) + + def create( + self, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKey: + """ + Create an organization admin API key + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/admin_api_keys", + body=maybe_transform({"name": name}, admin_api_key_create_params.AdminAPIKeyCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKey, + ) + + def retrieve( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKey: + """ + Retrieve a single organization API key + + Args: + key_id: The ID of the API key. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return self._get( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKey, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[AdminAPIKey]: + """ + List organization API keys + + Args: + after: Return keys with IDs that come after this ID in the pagination order. + + limit: Maximum number of keys to return. + + order: Order results by creation time, ascending or descending. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/admin_api_keys", + page=SyncCursorPage[AdminAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + admin_api_key_list_params.AdminAPIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AdminAPIKey, + ) + + def delete( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKeyDeleteResponse: + """ + Delete an organization admin API key + + Args: + key_id: The ID of the API key to be deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return self._delete( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKeyDeleteResponse, + ) + + +class AsyncAdminAPIKeys(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAdminAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAdminAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAdminAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAdminAPIKeysWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKey: + """ + Create an organization admin API key + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/admin_api_keys", + body=await async_maybe_transform({"name": name}, admin_api_key_create_params.AdminAPIKeyCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKey, + ) + + async def retrieve( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKey: + """ + Retrieve a single organization API key + + Args: + key_id: The ID of the API key. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return await self._get( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKey, + ) + + def list( + self, + *, + after: Optional[str] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[AdminAPIKey, AsyncCursorPage[AdminAPIKey]]: + """ + List organization API keys + + Args: + after: Return keys with IDs that come after this ID in the pagination order. + + limit: Maximum number of keys to return. + + order: Order results by creation time, ascending or descending. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/admin_api_keys", + page=AsyncCursorPage[AdminAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + admin_api_key_list_params.AdminAPIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=AdminAPIKey, + ) + + async def delete( + self, + key_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AdminAPIKeyDeleteResponse: + """ + Delete an organization admin API key + + Args: + key_id: The ID of the API key to be deleted. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return await self._delete( + path_template("/organization/admin_api_keys/{key_id}", key_id=key_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=AdminAPIKeyDeleteResponse, + ) + + +class AdminAPIKeysWithRawResponse: + def __init__(self, admin_api_keys: AdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = _legacy_response.to_raw_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + admin_api_keys.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + admin_api_keys.delete, + ) + + +class AsyncAdminAPIKeysWithRawResponse: + def __init__(self, admin_api_keys: AsyncAdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + admin_api_keys.delete, + ) + + +class AdminAPIKeysWithStreamingResponse: + def __init__(self, admin_api_keys: AdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = to_streamed_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = to_streamed_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = to_streamed_response_wrapper( + admin_api_keys.list, + ) + self.delete = to_streamed_response_wrapper( + admin_api_keys.delete, + ) + + +class AsyncAdminAPIKeysWithStreamingResponse: + def __init__(self, admin_api_keys: AsyncAdminAPIKeys) -> None: + self._admin_api_keys = admin_api_keys + + self.create = async_to_streamed_response_wrapper( + admin_api_keys.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + admin_api_keys.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + admin_api_keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + admin_api_keys.delete, + ) diff --git a/src/openai/resources/admin/organization/certificates.py b/src/openai/resources/admin/organization/certificates.py new file mode 100644 index 0000000000..1cab1e3610 --- /dev/null +++ b/src/openai/resources/admin/organization/certificates.py @@ -0,0 +1,815 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import ( + certificate_list_params, + certificate_create_params, + certificate_update_params, + certificate_activate_params, + certificate_retrieve_params, + certificate_deactivate_params, +) +from ....types.admin.organization.certificate import Certificate +from ....types.admin.organization.certificate_delete_response import CertificateDeleteResponse + +__all__ = ["Certificates", "AsyncCertificates"] + + +class Certificates(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CertificatesWithStreamingResponse(self) + + def create( + self, + *, + content: str, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Upload a certificate to the organization. + + This does **not** automatically + activate the certificate. + + Organizations can upload up to 50 certificates. + + Args: + content: The certificate content in PEM format + + name: An optional name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/certificates", + body=maybe_transform( + { + "content": content, + "name": name, + }, + certificate_create_params.CertificateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def retrieve( + self, + certificate_id: str, + *, + include: List[Literal["content"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """ + Get a certificate that has been uploaded to the organization. + + You can get a certificate regardless of whether it is active or not. + + Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `content` to fetch the PEM content of the certificate. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return self._get( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"include": include}, certificate_retrieve_params.CertificateRetrieveParams), + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def update( + self, + certificate_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Modify a certificate. + + Note that only the name can be modified. + + Args: + name: The updated name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return self._post( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + body=maybe_transform({"name": name}, certificate_update_params.CertificateUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Certificate]: + """ + List uploaded certificates for this organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates", + page=SyncConversationCursorPage[Certificate], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Certificate, + ) + + def delete( + self, + certificate_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CertificateDeleteResponse: + """ + Delete a certificate from the organization. + + The certificate must be inactive for the organization and all projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return self._delete( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=CertificateDeleteResponse, + ) + + def activate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPage[Certificate]: + """ + Activate certificates at the organization level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/activate", + page=SyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + def deactivate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPage[Certificate]: + """ + Deactivate certificates at the organization level. + + You can atomically and idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/deactivate", + page=SyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + +class AsyncCertificates(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCertificatesWithStreamingResponse(self) + + async def create( + self, + *, + content: str, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Upload a certificate to the organization. + + This does **not** automatically + activate the certificate. + + Organizations can upload up to 50 certificates. + + Args: + content: The certificate content in PEM format + + name: An optional name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/certificates", + body=await async_maybe_transform( + { + "content": content, + "name": name, + }, + certificate_create_params.CertificateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + async def retrieve( + self, + certificate_id: str, + *, + include: List[Literal["content"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """ + Get a certificate that has been uploaded to the organization. + + You can get a certificate regardless of whether it is active or not. + + Args: + include: A list of additional fields to include in the response. Currently the only + supported value is `content` to fetch the PEM content of the certificate. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return await self._get( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"include": include}, certificate_retrieve_params.CertificateRetrieveParams + ), + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + async def update( + self, + certificate_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Certificate: + """Modify a certificate. + + Note that only the name can be modified. + + Args: + name: The updated name for the certificate + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return await self._post( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + body=await async_maybe_transform({"name": name}, certificate_update_params.CertificateUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Certificate, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Certificate, AsyncConversationCursorPage[Certificate]]: + """ + List uploaded certificates for this organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates", + page=AsyncConversationCursorPage[Certificate], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Certificate, + ) + + async def delete( + self, + certificate_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CertificateDeleteResponse: + """ + Delete a certificate from the organization. + + The certificate must be inactive for the organization and all projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not certificate_id: + raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") + return await self._delete( + path_template("/organization/certificates/{certificate_id}", certificate_id=certificate_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=CertificateDeleteResponse, + ) + + def activate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + """ + Activate certificates at the organization level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/activate", + page=AsyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + def deactivate( + self, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + """ + Deactivate certificates at the organization level. + + You can atomically and idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/certificates/deactivate", + page=AsyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + +class CertificatesWithRawResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.create = _legacy_response.to_raw_response_wrapper( + certificates.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + certificates.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + certificates.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + certificates.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + certificates.delete, + ) + self.activate = _legacy_response.to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.to_raw_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithRawResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.create = _legacy_response.async_to_raw_response_wrapper( + certificates.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + certificates.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + certificates.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + certificates.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + certificates.delete, + ) + self.activate = _legacy_response.async_to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.async_to_raw_response_wrapper( + certificates.deactivate, + ) + + +class CertificatesWithStreamingResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.create = to_streamed_response_wrapper( + certificates.create, + ) + self.retrieve = to_streamed_response_wrapper( + certificates.retrieve, + ) + self.update = to_streamed_response_wrapper( + certificates.update, + ) + self.list = to_streamed_response_wrapper( + certificates.list, + ) + self.delete = to_streamed_response_wrapper( + certificates.delete, + ) + self.activate = to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = to_streamed_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithStreamingResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.create = async_to_streamed_response_wrapper( + certificates.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + certificates.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + certificates.update, + ) + self.list = async_to_streamed_response_wrapper( + certificates.list, + ) + self.delete = async_to_streamed_response_wrapper( + certificates.delete, + ) + self.activate = async_to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = async_to_streamed_response_wrapper( + certificates.deactivate, + ) diff --git a/src/openai/resources/admin/organization/groups/__init__.py b/src/openai/resources/admin/organization/groups/__init__.py new file mode 100644 index 0000000000..ffeb8b60ec --- /dev/null +++ b/src/openai/resources/admin/organization/groups/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from .groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) + +__all__ = [ + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", +] diff --git a/src/openai/resources/admin/organization/groups/groups.py b/src/openai/resources/admin/organization/groups/groups.py new file mode 100644 index 0000000000..1daf07b1ab --- /dev/null +++ b/src/openai/resources/admin/organization/groups/groups.py @@ -0,0 +1,544 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization import group_list_params, group_create_params, group_update_params +from .....types.admin.organization.group import Group +from .....types.admin.organization.group_delete_response import GroupDeleteResponse +from .....types.admin.organization.group_update_response import GroupUpdateResponse + +__all__ = ["Groups", "AsyncGroups"] + + +class Groups(SyncAPIResource): + @cached_property + def users(self) -> Users: + return Users(self._client) + + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> GroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return GroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return GroupsWithStreamingResponse(self) + + def create( + self, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Creates a new group in the organization. + + Args: + name: Human readable name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/groups", + body=maybe_transform({"name": name}, group_create_params.GroupCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + + def update( + self, + group_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupUpdateResponse: + """ + Updates a group's information. + + Args: + name: New display name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/organization/groups/{group_id}", group_id=group_id), + body=maybe_transform({"name": name}, group_update_params.GroupUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupUpdateResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Group]: + """ + Lists all groups in the organization. + + Args: + after: A cursor for use in pagination. `after` is a group ID that defines your place in + the list. For instance, if you make a list request and receive 100 objects, + ending with group_abc, your subsequent call can include `after=group_abc` in + order to fetch the next page of the list. + + limit: A limit on the number of groups to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/groups", + page=SyncCursorPage[Group], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Group, + ) + + def delete( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Deletes a group from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._delete( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class AsyncGroups(AsyncAPIResource): + @cached_property + def users(self) -> AsyncUsers: + return AsyncUsers(self._client) + + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncGroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncGroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncGroupsWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Creates a new group in the organization. + + Args: + name: Human readable name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/groups", + body=await async_maybe_transform({"name": name}, group_create_params.GroupCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + + async def update( + self, + group_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupUpdateResponse: + """ + Updates a group's information. + + Args: + name: New display name for the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/organization/groups/{group_id}", group_id=group_id), + body=await async_maybe_transform({"name": name}, group_update_params.GroupUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupUpdateResponse, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Group, AsyncCursorPage[Group]]: + """ + Lists all groups in the organization. + + Args: + after: A cursor for use in pagination. `after` is a group ID that defines your place in + the list. For instance, if you make a list request and receive 100 objects, + ending with group_abc, your subsequent call can include `after=group_abc` in + order to fetch the next page of the list. + + limit: A limit on the number of groups to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/groups", + page=AsyncCursorPage[Group], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Group, + ) + + async def delete( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Deletes a group from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._delete( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class GroupsWithRawResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = _legacy_response.to_raw_response_wrapper( + groups.create, + ) + self.update = _legacy_response.to_raw_response_wrapper( + groups.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> UsersWithRawResponse: + return UsersWithRawResponse(self._groups.users) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._groups.roles) + + +class AsyncGroupsWithRawResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = _legacy_response.async_to_raw_response_wrapper( + groups.create, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + groups.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> AsyncUsersWithRawResponse: + return AsyncUsersWithRawResponse(self._groups.users) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._groups.roles) + + +class GroupsWithStreamingResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = to_streamed_response_wrapper( + groups.create, + ) + self.update = to_streamed_response_wrapper( + groups.update, + ) + self.list = to_streamed_response_wrapper( + groups.list, + ) + self.delete = to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> UsersWithStreamingResponse: + return UsersWithStreamingResponse(self._groups.users) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._groups.roles) + + +class AsyncGroupsWithStreamingResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = async_to_streamed_response_wrapper( + groups.create, + ) + self.update = async_to_streamed_response_wrapper( + groups.update, + ) + self.list = async_to_streamed_response_wrapper( + groups.list, + ) + self.delete = async_to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def users(self) -> AsyncUsersWithStreamingResponse: + return AsyncUsersWithStreamingResponse(self._groups.users) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._groups.roles) diff --git a/src/openai/resources/admin/organization/groups/roles.py b/src/openai/resources/admin/organization/groups/roles.py new file mode 100644 index 0000000000..ca62ee9b35 --- /dev/null +++ b/src/openai/resources/admin/organization/groups/roles.py @@ -0,0 +1,398 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.groups import role_list_params, role_create_params +from .....types.admin.organization.groups.role_list_response import RoleListResponse +from .....types.admin.organization.groups.role_create_response import RoleCreateResponse +from .....types.admin.organization.groups.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + group_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a group within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[RoleListResponse]: + """ + Lists the organization roles assigned to a group within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + page=SyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a group within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + group_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a group within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + """ + Lists the organization roles assigned to a group within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/roles", group_id=group_id), + page=AsyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a group within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/src/openai/resources/admin/organization/groups/users.py b/src/openai/resources/admin/organization/groups/users.py new file mode 100644 index 0000000000..648d6221cf --- /dev/null +++ b/src/openai/resources/admin/organization/groups/users.py @@ -0,0 +1,400 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.groups import user_list_params, user_create_params +from .....types.admin.organization.organization_user import OrganizationUser +from .....types.admin.organization.groups.user_create_response import UserCreateResponse +from .....types.admin.organization.groups.user_delete_response import UserDeleteResponse + +__all__ = ["Users", "AsyncUsers"] + + +class Users(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsersWithStreamingResponse(self) + + def create( + self, + group_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserCreateResponse: + """ + Adds a user to a group. + + Args: + user_id: Identifier of the user to add to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + body=maybe_transform({"user_id": user_id}, user_create_params.UserCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserCreateResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[OrganizationUser]: + """ + Lists the users assigned to a group. + + Args: + after: A cursor for use in pagination. Provide the ID of the last user from the + previous list response to retrieve the next page. + + limit: A limit on the number of users to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of users in the list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + page=SyncCursorPage[OrganizationUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationUser, + ) + + def delete( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Removes a user from a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._delete( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class AsyncUsers(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsersWithStreamingResponse(self) + + async def create( + self, + group_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserCreateResponse: + """ + Adds a user to a group. + + Args: + user_id: Identifier of the user to add to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + body=await async_maybe_transform({"user_id": user_id}, user_create_params.UserCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserCreateResponse, + ) + + def list( + self, + group_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OrganizationUser, AsyncCursorPage[OrganizationUser]]: + """ + Lists the users assigned to a group. + + Args: + after: A cursor for use in pagination. Provide the ID of the last user from the + previous list response to retrieve the next page. + + limit: A limit on the number of users to be returned. Limit can range between 0 and + 1000, and the default is 100. + + order: Specifies the sort order of users in the list. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/organization/groups/{group_id}/users", group_id=group_id), + page=AsyncCursorPage[OrganizationUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationUser, + ) + + async def delete( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Removes a user from a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._delete( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class UsersWithRawResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = _legacy_response.to_raw_response_wrapper( + users.create, + ) + self.list = _legacy_response.to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + users.delete, + ) + + +class AsyncUsersWithRawResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = _legacy_response.async_to_raw_response_wrapper( + users.create, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + users.delete, + ) + + +class UsersWithStreamingResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = to_streamed_response_wrapper( + users.create, + ) + self.list = to_streamed_response_wrapper( + users.list, + ) + self.delete = to_streamed_response_wrapper( + users.delete, + ) + + +class AsyncUsersWithStreamingResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = async_to_streamed_response_wrapper( + users.create, + ) + self.list = async_to_streamed_response_wrapper( + users.list, + ) + self.delete = async_to_streamed_response_wrapper( + users.delete, + ) diff --git a/src/openai/resources/admin/organization/invites.py b/src/openai/resources/admin/organization/invites.py new file mode 100644 index 0000000000..23b83ccb18 --- /dev/null +++ b/src/openai/resources/admin/organization/invites.py @@ -0,0 +1,500 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import invite_list_params, invite_create_params +from ....types.admin.organization.invite import Invite +from ....types.admin.organization.invite_delete_response import InviteDeleteResponse + +__all__ = ["Invites", "AsyncInvites"] + + +class Invites(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InvitesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InvitesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InvitesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InvitesWithStreamingResponse(self) + + def create( + self, + *, + email: str, + role: Literal["reader", "owner"], + projects: Iterable[invite_create_params.Project] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Invite: + """Create an invite for a user to the organization. + + The invite must be accepted by + the user before they have access to the organization. + + Args: + email: Send an email to this address + + role: `owner` or `reader` + + projects: An array of projects to which membership is granted at the same time the org + invite is accepted. If omitted, the user will be invited to the default project + for compatibility with legacy behavior. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/invites", + body=maybe_transform( + { + "email": email, + "role": role, + "projects": projects, + }, + invite_create_params.InviteCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + def retrieve( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Invite: + """ + Retrieves an invite. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return self._get( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Invite]: + """ + Returns a list of invites in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/invites", + page=SyncConversationCursorPage[Invite], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + invite_list_params.InviteListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Invite, + ) + + def delete( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InviteDeleteResponse: + """Delete an invite. + + If the invite has already been accepted, it cannot be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return self._delete( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=InviteDeleteResponse, + ) + + +class AsyncInvites(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInvitesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInvitesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInvitesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInvitesWithStreamingResponse(self) + + async def create( + self, + *, + email: str, + role: Literal["reader", "owner"], + projects: Iterable[invite_create_params.Project] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Invite: + """Create an invite for a user to the organization. + + The invite must be accepted by + the user before they have access to the organization. + + Args: + email: Send an email to this address + + role: `owner` or `reader` + + projects: An array of projects to which membership is granted at the same time the org + invite is accepted. If omitted, the user will be invited to the default project + for compatibility with legacy behavior. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/invites", + body=await async_maybe_transform( + { + "email": email, + "role": role, + "projects": projects, + }, + invite_create_params.InviteCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + async def retrieve( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Invite: + """ + Retrieves an invite. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return await self._get( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Invite, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Invite, AsyncConversationCursorPage[Invite]]: + """ + Returns a list of invites in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/invites", + page=AsyncConversationCursorPage[Invite], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + invite_list_params.InviteListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Invite, + ) + + async def delete( + self, + invite_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InviteDeleteResponse: + """Delete an invite. + + If the invite has already been accepted, it cannot be deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not invite_id: + raise ValueError(f"Expected a non-empty value for `invite_id` but received {invite_id!r}") + return await self._delete( + path_template("/organization/invites/{invite_id}", invite_id=invite_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=InviteDeleteResponse, + ) + + +class InvitesWithRawResponse: + def __init__(self, invites: Invites) -> None: + self._invites = invites + + self.create = _legacy_response.to_raw_response_wrapper( + invites.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + invites.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + invites.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + invites.delete, + ) + + +class AsyncInvitesWithRawResponse: + def __init__(self, invites: AsyncInvites) -> None: + self._invites = invites + + self.create = _legacy_response.async_to_raw_response_wrapper( + invites.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + invites.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + invites.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + invites.delete, + ) + + +class InvitesWithStreamingResponse: + def __init__(self, invites: Invites) -> None: + self._invites = invites + + self.create = to_streamed_response_wrapper( + invites.create, + ) + self.retrieve = to_streamed_response_wrapper( + invites.retrieve, + ) + self.list = to_streamed_response_wrapper( + invites.list, + ) + self.delete = to_streamed_response_wrapper( + invites.delete, + ) + + +class AsyncInvitesWithStreamingResponse: + def __init__(self, invites: AsyncInvites) -> None: + self._invites = invites + + self.create = async_to_streamed_response_wrapper( + invites.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + invites.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + invites.list, + ) + self.delete = async_to_streamed_response_wrapper( + invites.delete, + ) diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py index 41dd0155d9..abbbadf575 100644 --- a/src/openai/resources/admin/organization/organization.py +++ b/src/openai/resources/admin/organization/organization.py @@ -2,6 +2,30 @@ from __future__ import annotations +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .usage import ( + Usage, + AsyncUsage, + UsageWithRawResponse, + AsyncUsageWithRawResponse, + UsageWithStreamingResponse, + AsyncUsageWithStreamingResponse, +) +from .invites import ( + Invites, + AsyncInvites, + InvitesWithRawResponse, + AsyncInvitesWithRawResponse, + InvitesWithStreamingResponse, + AsyncInvitesWithStreamingResponse, +) from ...._compat import cached_property from .audit_logs import ( AuditLogs, @@ -11,7 +35,47 @@ AuditLogsWithStreamingResponse, AsyncAuditLogsWithStreamingResponse, ) +from .users.users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) from ...._resource import SyncAPIResource, AsyncAPIResource +from .certificates import ( + Certificates, + AsyncCertificates, + CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse, +) +from .groups.groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) +from .admin_api_keys import ( + AdminAPIKeys, + AsyncAdminAPIKeys, + AdminAPIKeysWithRawResponse, + AsyncAdminAPIKeysWithRawResponse, + AdminAPIKeysWithStreamingResponse, + AsyncAdminAPIKeysWithStreamingResponse, +) +from .projects.projects import ( + Projects, + AsyncProjects, + ProjectsWithRawResponse, + AsyncProjectsWithRawResponse, + ProjectsWithStreamingResponse, + AsyncProjectsWithStreamingResponse, +) __all__ = ["Organization", "AsyncOrganization"] @@ -22,6 +86,38 @@ def audit_logs(self) -> AuditLogs: """List user actions and configuration changes within this organization.""" return AuditLogs(self._client) + @cached_property + def admin_api_keys(self) -> AdminAPIKeys: + return AdminAPIKeys(self._client) + + @cached_property + def usage(self) -> Usage: + return Usage(self._client) + + @cached_property + def invites(self) -> Invites: + return Invites(self._client) + + @cached_property + def users(self) -> Users: + return Users(self._client) + + @cached_property + def groups(self) -> Groups: + return Groups(self._client) + + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def certificates(self) -> Certificates: + return Certificates(self._client) + + @cached_property + def projects(self) -> Projects: + return Projects(self._client) + @cached_property def with_raw_response(self) -> OrganizationWithRawResponse: """ @@ -48,6 +144,38 @@ def audit_logs(self) -> AsyncAuditLogs: """List user actions and configuration changes within this organization.""" return AsyncAuditLogs(self._client) + @cached_property + def admin_api_keys(self) -> AsyncAdminAPIKeys: + return AsyncAdminAPIKeys(self._client) + + @cached_property + def usage(self) -> AsyncUsage: + return AsyncUsage(self._client) + + @cached_property + def invites(self) -> AsyncInvites: + return AsyncInvites(self._client) + + @cached_property + def users(self) -> AsyncUsers: + return AsyncUsers(self._client) + + @cached_property + def groups(self) -> AsyncGroups: + return AsyncGroups(self._client) + + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def certificates(self) -> AsyncCertificates: + return AsyncCertificates(self._client) + + @cached_property + def projects(self) -> AsyncProjects: + return AsyncProjects(self._client) + @cached_property def with_raw_response(self) -> AsyncOrganizationWithRawResponse: """ @@ -77,6 +205,38 @@ def audit_logs(self) -> AuditLogsWithRawResponse: """List user actions and configuration changes within this organization.""" return AuditLogsWithRawResponse(self._organization.audit_logs) + @cached_property + def admin_api_keys(self) -> AdminAPIKeysWithRawResponse: + return AdminAPIKeysWithRawResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> UsageWithRawResponse: + return UsageWithRawResponse(self._organization.usage) + + @cached_property + def invites(self) -> InvitesWithRawResponse: + return InvitesWithRawResponse(self._organization.invites) + + @cached_property + def users(self) -> UsersWithRawResponse: + return UsersWithRawResponse(self._organization.users) + + @cached_property + def groups(self) -> GroupsWithRawResponse: + return GroupsWithRawResponse(self._organization.groups) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._organization.roles) + + @cached_property + def certificates(self) -> CertificatesWithRawResponse: + return CertificatesWithRawResponse(self._organization.certificates) + + @cached_property + def projects(self) -> ProjectsWithRawResponse: + return ProjectsWithRawResponse(self._organization.projects) + class AsyncOrganizationWithRawResponse: def __init__(self, organization: AsyncOrganization) -> None: @@ -87,6 +247,38 @@ def audit_logs(self) -> AsyncAuditLogsWithRawResponse: """List user actions and configuration changes within this organization.""" return AsyncAuditLogsWithRawResponse(self._organization.audit_logs) + @cached_property + def admin_api_keys(self) -> AsyncAdminAPIKeysWithRawResponse: + return AsyncAdminAPIKeysWithRawResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> AsyncUsageWithRawResponse: + return AsyncUsageWithRawResponse(self._organization.usage) + + @cached_property + def invites(self) -> AsyncInvitesWithRawResponse: + return AsyncInvitesWithRawResponse(self._organization.invites) + + @cached_property + def users(self) -> AsyncUsersWithRawResponse: + return AsyncUsersWithRawResponse(self._organization.users) + + @cached_property + def groups(self) -> AsyncGroupsWithRawResponse: + return AsyncGroupsWithRawResponse(self._organization.groups) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._organization.roles) + + @cached_property + def certificates(self) -> AsyncCertificatesWithRawResponse: + return AsyncCertificatesWithRawResponse(self._organization.certificates) + + @cached_property + def projects(self) -> AsyncProjectsWithRawResponse: + return AsyncProjectsWithRawResponse(self._organization.projects) + class OrganizationWithStreamingResponse: def __init__(self, organization: Organization) -> None: @@ -97,6 +289,38 @@ def audit_logs(self) -> AuditLogsWithStreamingResponse: """List user actions and configuration changes within this organization.""" return AuditLogsWithStreamingResponse(self._organization.audit_logs) + @cached_property + def admin_api_keys(self) -> AdminAPIKeysWithStreamingResponse: + return AdminAPIKeysWithStreamingResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> UsageWithStreamingResponse: + return UsageWithStreamingResponse(self._organization.usage) + + @cached_property + def invites(self) -> InvitesWithStreamingResponse: + return InvitesWithStreamingResponse(self._organization.invites) + + @cached_property + def users(self) -> UsersWithStreamingResponse: + return UsersWithStreamingResponse(self._organization.users) + + @cached_property + def groups(self) -> GroupsWithStreamingResponse: + return GroupsWithStreamingResponse(self._organization.groups) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._organization.roles) + + @cached_property + def certificates(self) -> CertificatesWithStreamingResponse: + return CertificatesWithStreamingResponse(self._organization.certificates) + + @cached_property + def projects(self) -> ProjectsWithStreamingResponse: + return ProjectsWithStreamingResponse(self._organization.projects) + class AsyncOrganizationWithStreamingResponse: def __init__(self, organization: AsyncOrganization) -> None: @@ -106,3 +330,35 @@ def __init__(self, organization: AsyncOrganization) -> None: def audit_logs(self) -> AsyncAuditLogsWithStreamingResponse: """List user actions and configuration changes within this organization.""" return AsyncAuditLogsWithStreamingResponse(self._organization.audit_logs) + + @cached_property + def admin_api_keys(self) -> AsyncAdminAPIKeysWithStreamingResponse: + return AsyncAdminAPIKeysWithStreamingResponse(self._organization.admin_api_keys) + + @cached_property + def usage(self) -> AsyncUsageWithStreamingResponse: + return AsyncUsageWithStreamingResponse(self._organization.usage) + + @cached_property + def invites(self) -> AsyncInvitesWithStreamingResponse: + return AsyncInvitesWithStreamingResponse(self._organization.invites) + + @cached_property + def users(self) -> AsyncUsersWithStreamingResponse: + return AsyncUsersWithStreamingResponse(self._organization.users) + + @cached_property + def groups(self) -> AsyncGroupsWithStreamingResponse: + return AsyncGroupsWithStreamingResponse(self._organization.groups) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._organization.roles) + + @cached_property + def certificates(self) -> AsyncCertificatesWithStreamingResponse: + return AsyncCertificatesWithStreamingResponse(self._organization.certificates) + + @cached_property + def projects(self) -> AsyncProjectsWithStreamingResponse: + return AsyncProjectsWithStreamingResponse(self._organization.projects) diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py new file mode 100644 index 0000000000..a64326bfa9 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/__init__.py @@ -0,0 +1,117 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from .groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) +from .api_keys import ( + APIKeys, + AsyncAPIKeys, + APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse, +) +from .projects import ( + Projects, + AsyncProjects, + ProjectsWithRawResponse, + AsyncProjectsWithRawResponse, + ProjectsWithStreamingResponse, + AsyncProjectsWithStreamingResponse, +) +from .rate_limits import ( + RateLimits, + AsyncRateLimits, + RateLimitsWithRawResponse, + AsyncRateLimitsWithRawResponse, + RateLimitsWithStreamingResponse, + AsyncRateLimitsWithStreamingResponse, +) +from .certificates import ( + Certificates, + AsyncCertificates, + CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse, +) +from .service_accounts import ( + ServiceAccounts, + AsyncServiceAccounts, + ServiceAccountsWithRawResponse, + AsyncServiceAccountsWithRawResponse, + ServiceAccountsWithStreamingResponse, + AsyncServiceAccountsWithStreamingResponse, +) + +__all__ = [ + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", + "ServiceAccounts", + "AsyncServiceAccounts", + "ServiceAccountsWithRawResponse", + "AsyncServiceAccountsWithRawResponse", + "ServiceAccountsWithStreamingResponse", + "AsyncServiceAccountsWithStreamingResponse", + "APIKeys", + "AsyncAPIKeys", + "APIKeysWithRawResponse", + "AsyncAPIKeysWithRawResponse", + "APIKeysWithStreamingResponse", + "AsyncAPIKeysWithStreamingResponse", + "RateLimits", + "AsyncRateLimits", + "RateLimitsWithRawResponse", + "AsyncRateLimitsWithRawResponse", + "RateLimitsWithStreamingResponse", + "AsyncRateLimitsWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Certificates", + "AsyncCertificates", + "CertificatesWithRawResponse", + "AsyncCertificatesWithRawResponse", + "CertificatesWithStreamingResponse", + "AsyncCertificatesWithStreamingResponse", + "Projects", + "AsyncProjects", + "ProjectsWithRawResponse", + "AsyncProjectsWithRawResponse", + "ProjectsWithStreamingResponse", + "AsyncProjectsWithStreamingResponse", +] diff --git a/src/openai/resources/admin/organization/projects/api_keys.py b/src/openai/resources/admin/organization/projects/api_keys.py new file mode 100644 index 0000000000..8902ce56ff --- /dev/null +++ b/src/openai/resources/admin/organization/projects/api_keys.py @@ -0,0 +1,405 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import api_key_list_params +from .....types.admin.organization.projects.project_api_key import ProjectAPIKey +from .....types.admin.organization.projects.api_key_delete_response import APIKeyDeleteResponse + +__all__ = ["APIKeys", "AsyncAPIKeys"] + + +class APIKeys(SyncAPIResource): + @cached_property + def with_raw_response(self) -> APIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return APIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> APIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return APIKeysWithStreamingResponse(self) + + def retrieve( + self, + key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKey: + """ + Retrieves an API key in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectAPIKey, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectAPIKey]: + """ + Returns a list of API keys in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/api_keys", project_id=project_id), + page=SyncConversationCursorPage[ProjectAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + api_key_list_params.APIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectAPIKey, + ) + + def delete( + self, + key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + Deletes an API key from the project. + + Returns confirmation of the key deletion, or an error if the key belonged to a + service account. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyDeleteResponse, + ) + + +class AsyncAPIKeys(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAPIKeysWithStreamingResponse(self) + + async def retrieve( + self, + key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKey: + """ + Retrieves an API key in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectAPIKey, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectAPIKey, AsyncConversationCursorPage[ProjectAPIKey]]: + """ + Returns a list of API keys in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/api_keys", project_id=project_id), + page=AsyncConversationCursorPage[ProjectAPIKey], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + api_key_list_params.APIKeyListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectAPIKey, + ) + + async def delete( + self, + key_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyDeleteResponse: + """ + Deletes an API key from the project. + + Returns confirmation of the key deletion, or an error if the key belonged to a + service account. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not key_id: + raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyDeleteResponse, + ) + + +class APIKeysWithRawResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = _legacy_response.to_raw_response_wrapper( + api_keys.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + api_keys.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + api_keys.delete, + ) + + +class AsyncAPIKeysWithRawResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + api_keys.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + api_keys.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + api_keys.delete, + ) + + +class APIKeysWithStreamingResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = to_streamed_response_wrapper( + api_keys.retrieve, + ) + self.list = to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = to_streamed_response_wrapper( + api_keys.delete, + ) + + +class AsyncAPIKeysWithStreamingResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.retrieve = async_to_streamed_response_wrapper( + api_keys.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + api_keys.list, + ) + self.delete = async_to_streamed_response_wrapper( + api_keys.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/certificates.py b/src/openai/resources/admin/organization/projects/certificates.py new file mode 100644 index 0000000000..3ff2111293 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/certificates.py @@ -0,0 +1,426 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import ( + certificate_list_params, + certificate_activate_params, + certificate_deactivate_params, +) +from .....types.admin.organization.certificate import Certificate + +__all__ = ["Certificates", "AsyncCertificates"] + + +class Certificates(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return CertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return CertificatesWithStreamingResponse(self) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Certificate]: + """ + List certificates for this project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates", project_id=project_id), + page=SyncConversationCursorPage[Certificate], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Certificate, + ) + + def activate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPage[Certificate]: + """ + Activate certificates at the project level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/activate", project_id=project_id), + page=SyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + def deactivate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncPage[Certificate]: + """Deactivate certificates at the project level. + + You can atomically and + idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/deactivate", project_id=project_id), + page=SyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + +class AsyncCertificates(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCertificatesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncCertificatesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCertificatesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncCertificatesWithStreamingResponse(self) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Certificate, AsyncConversationCursorPage[Certificate]]: + """ + List certificates for this project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending + order and `desc` for descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates", project_id=project_id), + page=AsyncConversationCursorPage[Certificate], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + certificate_list_params.CertificateListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Certificate, + ) + + def activate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + """ + Activate certificates at the project level. + + You can atomically and idempotently activate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/activate", project_id=project_id), + page=AsyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + def deactivate( + self, + project_id: str, + *, + certificate_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + """Deactivate certificates at the project level. + + You can atomically and + idempotently deactivate up to 10 certificates at a time. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/certificates/deactivate", project_id=project_id), + page=AsyncPage[Certificate], + body=maybe_transform( + {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + model=Certificate, + method="post", + ) + + +class CertificatesWithRawResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.list = _legacy_response.to_raw_response_wrapper( + certificates.list, + ) + self.activate = _legacy_response.to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.to_raw_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithRawResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.list = _legacy_response.async_to_raw_response_wrapper( + certificates.list, + ) + self.activate = _legacy_response.async_to_raw_response_wrapper( + certificates.activate, + ) + self.deactivate = _legacy_response.async_to_raw_response_wrapper( + certificates.deactivate, + ) + + +class CertificatesWithStreamingResponse: + def __init__(self, certificates: Certificates) -> None: + self._certificates = certificates + + self.list = to_streamed_response_wrapper( + certificates.list, + ) + self.activate = to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = to_streamed_response_wrapper( + certificates.deactivate, + ) + + +class AsyncCertificatesWithStreamingResponse: + def __init__(self, certificates: AsyncCertificates) -> None: + self._certificates = certificates + + self.list = async_to_streamed_response_wrapper( + certificates.list, + ) + self.activate = async_to_streamed_response_wrapper( + certificates.activate, + ) + self.deactivate = async_to_streamed_response_wrapper( + certificates.deactivate, + ) diff --git a/src/openai/resources/admin/organization/projects/groups/__init__.py b/src/openai/resources/admin/organization/projects/groups/__init__.py new file mode 100644 index 0000000000..4feb66239f --- /dev/null +++ b/src/openai/resources/admin/organization/projects/groups/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) + +__all__ = [ + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Groups", + "AsyncGroups", + "GroupsWithRawResponse", + "AsyncGroupsWithRawResponse", + "GroupsWithStreamingResponse", + "AsyncGroupsWithStreamingResponse", +] diff --git a/src/openai/resources/admin/organization/projects/groups/groups.py b/src/openai/resources/admin/organization/projects/groups/groups.py new file mode 100644 index 0000000000..8525a65255 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/groups/groups.py @@ -0,0 +1,451 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncCursorPage, AsyncCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects import group_list_params, group_create_params +from ......types.admin.organization.projects.project_group import ProjectGroup +from ......types.admin.organization.projects.group_delete_response import GroupDeleteResponse + +__all__ = ["Groups", "AsyncGroups"] + + +class Groups(SyncAPIResource): + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> GroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return GroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return GroupsWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + group_id: str, + role: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Grants a group access to a project. + + Args: + group_id: Identifier of the group to add to the project. + + role: Identifier of the project role to grant to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + body=maybe_transform( + { + "group_id": group_id, + "role": role, + }, + group_create_params.GroupCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[ProjectGroup]: + """ + Lists the groups that have access to a project. + + Args: + after: Cursor for pagination. Provide the ID of the last group from the previous + response to fetch the next page. + + limit: A limit on the number of project groups to return. Defaults to 20. + + order: Sort order for the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + page=SyncCursorPage[ProjectGroup], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectGroup, + ) + + def delete( + self, + group_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Revokes a group's access to a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class AsyncGroups(AsyncAPIResource): + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncGroupsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncGroupsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGroupsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncGroupsWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + group_id: str, + role: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Grants a group access to a project. + + Args: + group_id: Identifier of the group to add to the project. + + role: Identifier of the project role to grant to the group. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + body=await async_maybe_transform( + { + "group_id": group_id, + "role": role, + }, + group_create_params.GroupCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectGroup, AsyncCursorPage[ProjectGroup]]: + """ + Lists the groups that have access to a project. + + Args: + after: Cursor for pagination. Provide the ID of the last group from the previous + response to fetch the next page. + + limit: A limit on the number of project groups to return. Defaults to 20. + + order: Sort order for the returned groups. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/groups", project_id=project_id), + page=AsyncCursorPage[ProjectGroup], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + group_list_params.GroupListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectGroup, + ) + + async def delete( + self, + group_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GroupDeleteResponse: + """ + Revokes a group's access to a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=GroupDeleteResponse, + ) + + +class GroupsWithRawResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = _legacy_response.to_raw_response_wrapper( + groups.create, + ) + self.list = _legacy_response.to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._groups.roles) + + +class AsyncGroupsWithRawResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = _legacy_response.async_to_raw_response_wrapper( + groups.create, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + groups.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._groups.roles) + + +class GroupsWithStreamingResponse: + def __init__(self, groups: Groups) -> None: + self._groups = groups + + self.create = to_streamed_response_wrapper( + groups.create, + ) + self.list = to_streamed_response_wrapper( + groups.list, + ) + self.delete = to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._groups.roles) + + +class AsyncGroupsWithStreamingResponse: + def __init__(self, groups: AsyncGroups) -> None: + self._groups = groups + + self.create = async_to_streamed_response_wrapper( + groups.create, + ) + self.list = async_to_streamed_response_wrapper( + groups.list, + ) + self.delete = async_to_streamed_response_wrapper( + groups.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._groups.roles) diff --git a/src/openai/resources/admin/organization/projects/groups/roles.py b/src/openai/resources/admin/organization/projects/groups/roles.py new file mode 100644 index 0000000000..20cab5d0e4 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/groups/roles.py @@ -0,0 +1,426 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...... import _legacy_response +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncCursorPage, AsyncCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects.groups import role_list_params, role_create_params +from ......types.admin.organization.projects.groups.role_list_response import RoleListResponse +from ......types.admin.organization.projects.groups.role_create_response import RoleCreateResponse +from ......types.admin.organization.projects.groups.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + group_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a group within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._post( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + group_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[RoleListResponse]: + """ + Lists the project roles assigned to a group within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + page=SyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a group within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + group_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a group within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._post( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + group_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + """ + Lists the project roles assigned to a group within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), + page=AsyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a group within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py new file mode 100644 index 0000000000..db774338d7 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -0,0 +1,824 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .api_keys import ( + APIKeys, + AsyncAPIKeys, + APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from .rate_limits import ( + RateLimits, + AsyncRateLimits, + RateLimitsWithRawResponse, + AsyncRateLimitsWithRawResponse, + RateLimitsWithStreamingResponse, + AsyncRateLimitsWithStreamingResponse, +) +from .users.users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) +from .certificates import ( + Certificates, + AsyncCertificates, + CertificatesWithRawResponse, + AsyncCertificatesWithRawResponse, + CertificatesWithStreamingResponse, + AsyncCertificatesWithStreamingResponse, +) +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .groups.groups import ( + Groups, + AsyncGroups, + GroupsWithRawResponse, + AsyncGroupsWithRawResponse, + GroupsWithStreamingResponse, + AsyncGroupsWithStreamingResponse, +) +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .service_accounts import ( + ServiceAccounts, + AsyncServiceAccounts, + ServiceAccountsWithRawResponse, + AsyncServiceAccountsWithRawResponse, + ServiceAccountsWithStreamingResponse, + AsyncServiceAccountsWithStreamingResponse, +) +from .....types.admin.organization import project_list_params, project_create_params, project_update_params +from .....types.admin.organization.project import Project + +__all__ = ["Projects", "AsyncProjects"] + + +class Projects(SyncAPIResource): + @cached_property + def users(self) -> Users: + return Users(self._client) + + @cached_property + def service_accounts(self) -> ServiceAccounts: + return ServiceAccounts(self._client) + + @cached_property + def api_keys(self) -> APIKeys: + return APIKeys(self._client) + + @cached_property + def rate_limits(self) -> RateLimits: + return RateLimits(self._client) + + @cached_property + def groups(self) -> Groups: + return Groups(self._client) + + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def certificates(self) -> Certificates: + return Certificates(self._client) + + @cached_property + def with_raw_response(self) -> ProjectsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ProjectsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProjectsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ProjectsWithStreamingResponse(self) + + def create( + self, + *, + name: str, + geography: Literal["US", "EU", "JP", "IN", "KR", "CA", "AU", "SG"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """Create a new project in the organization. + + Projects can be created and archived, + but cannot be deleted. + + Args: + name: The friendly name of the project, this name appears in reports. + + geography: Create the project with the specified data residency region. Your organization + must have access to Data residency functionality in order to use. See + [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/projects", + body=maybe_transform( + { + "name": name, + "geography": geography, + }, + project_create_params.ProjectCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Retrieves a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def update( + self, + project_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Modifies a project in the organization. + + Args: + name: The updated name of the project, this name appears in reports. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}", project_id=project_id), + body=maybe_transform({"name": name}, project_update_params.ProjectUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def list( + self, + *, + after: str | Omit = omit, + include_archived: bool | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[Project]: + """Returns a list of projects. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + include_archived: If `true` returns all projects including those that have been `archived`. + Archived projects are not included by default. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/projects", + page=SyncConversationCursorPage[Project], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include_archived": include_archived, + "limit": limit, + }, + project_list_params.ProjectListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Project, + ) + + def archive( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """Archives a project in the organization. + + Archived projects cannot be used or + updated. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/archive", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + +class AsyncProjects(AsyncAPIResource): + @cached_property + def users(self) -> AsyncUsers: + return AsyncUsers(self._client) + + @cached_property + def service_accounts(self) -> AsyncServiceAccounts: + return AsyncServiceAccounts(self._client) + + @cached_property + def api_keys(self) -> AsyncAPIKeys: + return AsyncAPIKeys(self._client) + + @cached_property + def rate_limits(self) -> AsyncRateLimits: + return AsyncRateLimits(self._client) + + @cached_property + def groups(self) -> AsyncGroups: + return AsyncGroups(self._client) + + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def certificates(self) -> AsyncCertificates: + return AsyncCertificates(self._client) + + @cached_property + def with_raw_response(self) -> AsyncProjectsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncProjectsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProjectsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncProjectsWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + geography: Literal["US", "EU", "JP", "IN", "KR", "CA", "AU", "SG"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """Create a new project in the organization. + + Projects can be created and archived, + but cannot be deleted. + + Args: + name: The friendly name of the project, this name appears in reports. + + geography: Create the project with the specified data residency region. Your organization + must have access to Data residency functionality in order to use. See + [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/projects", + body=await async_maybe_transform( + { + "name": name, + "geography": geography, + }, + project_create_params.ProjectCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Retrieves a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + async def update( + self, + project_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Modifies a project in the organization. + + Args: + name: The updated name of the project, this name appears in reports. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}", project_id=project_id), + body=await async_maybe_transform({"name": name}, project_update_params.ProjectUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + def list( + self, + *, + after: str | Omit = omit, + include_archived: bool | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Project, AsyncConversationCursorPage[Project]]: + """Returns a list of projects. + + Args: + after: A cursor for use in pagination. + + `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + include_archived: If `true` returns all projects including those that have been `archived`. + Archived projects are not included by default. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/projects", + page=AsyncConversationCursorPage[Project], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include_archived": include_archived, + "limit": limit, + }, + project_list_params.ProjectListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Project, + ) + + async def archive( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """Archives a project in the organization. + + Archived projects cannot be used or + updated. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/archive", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Project, + ) + + +class ProjectsWithRawResponse: + def __init__(self, projects: Projects) -> None: + self._projects = projects + + self.create = _legacy_response.to_raw_response_wrapper( + projects.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + projects.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + projects.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + projects.list, + ) + self.archive = _legacy_response.to_raw_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> UsersWithRawResponse: + return UsersWithRawResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> ServiceAccountsWithRawResponse: + return ServiceAccountsWithRawResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> APIKeysWithRawResponse: + return APIKeysWithRawResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> RateLimitsWithRawResponse: + return RateLimitsWithRawResponse(self._projects.rate_limits) + + @cached_property + def groups(self) -> GroupsWithRawResponse: + return GroupsWithRawResponse(self._projects.groups) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._projects.roles) + + @cached_property + def certificates(self) -> CertificatesWithRawResponse: + return CertificatesWithRawResponse(self._projects.certificates) + + +class AsyncProjectsWithRawResponse: + def __init__(self, projects: AsyncProjects) -> None: + self._projects = projects + + self.create = _legacy_response.async_to_raw_response_wrapper( + projects.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + projects.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + projects.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + projects.list, + ) + self.archive = _legacy_response.async_to_raw_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> AsyncUsersWithRawResponse: + return AsyncUsersWithRawResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> AsyncServiceAccountsWithRawResponse: + return AsyncServiceAccountsWithRawResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> AsyncAPIKeysWithRawResponse: + return AsyncAPIKeysWithRawResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> AsyncRateLimitsWithRawResponse: + return AsyncRateLimitsWithRawResponse(self._projects.rate_limits) + + @cached_property + def groups(self) -> AsyncGroupsWithRawResponse: + return AsyncGroupsWithRawResponse(self._projects.groups) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._projects.roles) + + @cached_property + def certificates(self) -> AsyncCertificatesWithRawResponse: + return AsyncCertificatesWithRawResponse(self._projects.certificates) + + +class ProjectsWithStreamingResponse: + def __init__(self, projects: Projects) -> None: + self._projects = projects + + self.create = to_streamed_response_wrapper( + projects.create, + ) + self.retrieve = to_streamed_response_wrapper( + projects.retrieve, + ) + self.update = to_streamed_response_wrapper( + projects.update, + ) + self.list = to_streamed_response_wrapper( + projects.list, + ) + self.archive = to_streamed_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> UsersWithStreamingResponse: + return UsersWithStreamingResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> ServiceAccountsWithStreamingResponse: + return ServiceAccountsWithStreamingResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> APIKeysWithStreamingResponse: + return APIKeysWithStreamingResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> RateLimitsWithStreamingResponse: + return RateLimitsWithStreamingResponse(self._projects.rate_limits) + + @cached_property + def groups(self) -> GroupsWithStreamingResponse: + return GroupsWithStreamingResponse(self._projects.groups) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._projects.roles) + + @cached_property + def certificates(self) -> CertificatesWithStreamingResponse: + return CertificatesWithStreamingResponse(self._projects.certificates) + + +class AsyncProjectsWithStreamingResponse: + def __init__(self, projects: AsyncProjects) -> None: + self._projects = projects + + self.create = async_to_streamed_response_wrapper( + projects.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + projects.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + projects.update, + ) + self.list = async_to_streamed_response_wrapper( + projects.list, + ) + self.archive = async_to_streamed_response_wrapper( + projects.archive, + ) + + @cached_property + def users(self) -> AsyncUsersWithStreamingResponse: + return AsyncUsersWithStreamingResponse(self._projects.users) + + @cached_property + def service_accounts(self) -> AsyncServiceAccountsWithStreamingResponse: + return AsyncServiceAccountsWithStreamingResponse(self._projects.service_accounts) + + @cached_property + def api_keys(self) -> AsyncAPIKeysWithStreamingResponse: + return AsyncAPIKeysWithStreamingResponse(self._projects.api_keys) + + @cached_property + def rate_limits(self) -> AsyncRateLimitsWithStreamingResponse: + return AsyncRateLimitsWithStreamingResponse(self._projects.rate_limits) + + @cached_property + def groups(self) -> AsyncGroupsWithStreamingResponse: + return AsyncGroupsWithStreamingResponse(self._projects.groups) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._projects.roles) + + @cached_property + def certificates(self) -> AsyncCertificatesWithStreamingResponse: + return AsyncCertificatesWithStreamingResponse(self._projects.certificates) diff --git a/src/openai/resources/admin/organization/projects/rate_limits.py b/src/openai/resources/admin/organization/projects/rate_limits.py new file mode 100644 index 0000000000..9fe20f572e --- /dev/null +++ b/src/openai/resources/admin/organization/projects/rate_limits.py @@ -0,0 +1,379 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import ( + rate_limit_list_rate_limits_params, + rate_limit_update_rate_limit_params, +) +from .....types.admin.organization.projects.project_rate_limit import ProjectRateLimit + +__all__ = ["RateLimits", "AsyncRateLimits"] + + +class RateLimits(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RateLimitsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RateLimitsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RateLimitsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RateLimitsWithStreamingResponse(self) + + def list_rate_limits( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectRateLimit]: + """ + Returns the rate limits per model for a project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + beginning with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. The default is 100. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/rate_limits", project_id=project_id), + page=SyncConversationCursorPage[ProjectRateLimit], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + }, + rate_limit_list_rate_limits_params.RateLimitListRateLimitsParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectRateLimit, + ) + + def update_rate_limit( + self, + rate_limit_id: str, + *, + project_id: str, + batch_1_day_max_input_tokens: int | Omit = omit, + max_audio_megabytes_per_1_minute: int | Omit = omit, + max_images_per_1_minute: int | Omit = omit, + max_requests_per_1_day: int | Omit = omit, + max_requests_per_1_minute: int | Omit = omit, + max_tokens_per_1_minute: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectRateLimit: + """ + Updates a project rate limit. + + Args: + batch_1_day_max_input_tokens: The maximum batch input tokens per day. Only relevant for certain models. + + max_audio_megabytes_per_1_minute: The maximum audio megabytes per minute. Only relevant for certain models. + + max_images_per_1_minute: The maximum images per minute. Only relevant for certain models. + + max_requests_per_1_day: The maximum requests per day. Only relevant for certain models. + + max_requests_per_1_minute: The maximum requests per minute. + + max_tokens_per_1_minute: The maximum tokens per minute. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not rate_limit_id: + raise ValueError(f"Expected a non-empty value for `rate_limit_id` but received {rate_limit_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/rate_limits/{rate_limit_id}", + project_id=project_id, + rate_limit_id=rate_limit_id, + ), + body=maybe_transform( + { + "batch_1_day_max_input_tokens": batch_1_day_max_input_tokens, + "max_audio_megabytes_per_1_minute": max_audio_megabytes_per_1_minute, + "max_images_per_1_minute": max_images_per_1_minute, + "max_requests_per_1_day": max_requests_per_1_day, + "max_requests_per_1_minute": max_requests_per_1_minute, + "max_tokens_per_1_minute": max_tokens_per_1_minute, + }, + rate_limit_update_rate_limit_params.RateLimitUpdateRateLimitParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectRateLimit, + ) + + +class AsyncRateLimits(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRateLimitsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRateLimitsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRateLimitsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRateLimitsWithStreamingResponse(self) + + def list_rate_limits( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectRateLimit, AsyncConversationCursorPage[ProjectRateLimit]]: + """ + Returns the rate limits per model for a project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + before: A cursor for use in pagination. `before` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + beginning with obj_foo, your subsequent call can include before=obj_foo in order + to fetch the previous page of the list. + + limit: A limit on the number of objects to be returned. The default is 100. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/rate_limits", project_id=project_id), + page=AsyncConversationCursorPage[ProjectRateLimit], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + }, + rate_limit_list_rate_limits_params.RateLimitListRateLimitsParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectRateLimit, + ) + + async def update_rate_limit( + self, + rate_limit_id: str, + *, + project_id: str, + batch_1_day_max_input_tokens: int | Omit = omit, + max_audio_megabytes_per_1_minute: int | Omit = omit, + max_images_per_1_minute: int | Omit = omit, + max_requests_per_1_day: int | Omit = omit, + max_requests_per_1_minute: int | Omit = omit, + max_tokens_per_1_minute: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectRateLimit: + """ + Updates a project rate limit. + + Args: + batch_1_day_max_input_tokens: The maximum batch input tokens per day. Only relevant for certain models. + + max_audio_megabytes_per_1_minute: The maximum audio megabytes per minute. Only relevant for certain models. + + max_images_per_1_minute: The maximum images per minute. Only relevant for certain models. + + max_requests_per_1_day: The maximum requests per day. Only relevant for certain models. + + max_requests_per_1_minute: The maximum requests per minute. + + max_tokens_per_1_minute: The maximum tokens per minute. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not rate_limit_id: + raise ValueError(f"Expected a non-empty value for `rate_limit_id` but received {rate_limit_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/rate_limits/{rate_limit_id}", + project_id=project_id, + rate_limit_id=rate_limit_id, + ), + body=await async_maybe_transform( + { + "batch_1_day_max_input_tokens": batch_1_day_max_input_tokens, + "max_audio_megabytes_per_1_minute": max_audio_megabytes_per_1_minute, + "max_images_per_1_minute": max_images_per_1_minute, + "max_requests_per_1_day": max_requests_per_1_day, + "max_requests_per_1_minute": max_requests_per_1_minute, + "max_tokens_per_1_minute": max_tokens_per_1_minute, + }, + rate_limit_update_rate_limit_params.RateLimitUpdateRateLimitParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectRateLimit, + ) + + +class RateLimitsWithRawResponse: + def __init__(self, rate_limits: RateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = _legacy_response.to_raw_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = _legacy_response.to_raw_response_wrapper( + rate_limits.update_rate_limit, + ) + + +class AsyncRateLimitsWithRawResponse: + def __init__(self, rate_limits: AsyncRateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = _legacy_response.async_to_raw_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = _legacy_response.async_to_raw_response_wrapper( + rate_limits.update_rate_limit, + ) + + +class RateLimitsWithStreamingResponse: + def __init__(self, rate_limits: RateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = to_streamed_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = to_streamed_response_wrapper( + rate_limits.update_rate_limit, + ) + + +class AsyncRateLimitsWithStreamingResponse: + def __init__(self, rate_limits: AsyncRateLimits) -> None: + self._rate_limits = rate_limits + + self.list_rate_limits = async_to_streamed_response_wrapper( + rate_limits.list_rate_limits, + ) + self.update_rate_limit = async_to_streamed_response_wrapper( + rate_limits.update_rate_limit, + ) diff --git a/src/openai/resources/admin/organization/projects/roles.py b/src/openai/resources/admin/organization/projects/roles.py new file mode 100644 index 0000000000..4242afe052 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/roles.py @@ -0,0 +1,552 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.role import Role +from .....types.admin.organization.projects import role_list_params, role_create_params, role_update_params +from .....types.admin.organization.projects.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for a project. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/projects/{project_id}/roles", project_id=project_id), + body=maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def update( + self, + role_id: str, + *, + project_id: str, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing project role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._post( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + body=maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Role]: + """Lists the roles configured for a project. + + Args: + after: Cursor for pagination. + + Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/roles", project_id=project_id), + page=SyncCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + def delete( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for a project. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/projects/{project_id}/roles", project_id=project_id), + body=await async_maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + async def update( + self, + role_id: str, + *, + project_id: str, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing project role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._post( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + body=await async_maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Role, AsyncCursorPage[Role]]: + """Lists the roles configured for a project. + + Args: + after: Cursor for pagination. + + Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/roles", project_id=project_id), + page=AsyncCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + async def delete( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.update = _legacy_response.to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.update = to_streamed_response_wrapper( + roles.update, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.update = async_to_streamed_response_wrapper( + roles.update, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/service_accounts.py b/src/openai/resources/admin/organization/projects/service_accounts.py new file mode 100644 index 0000000000..9c265fd766 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/service_accounts.py @@ -0,0 +1,512 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import service_account_list_params, service_account_create_params +from .....types.admin.organization.projects.project_service_account import ProjectServiceAccount +from .....types.admin.organization.projects.service_account_create_response import ServiceAccountCreateResponse +from .....types.admin.organization.projects.service_account_delete_response import ServiceAccountDeleteResponse + +__all__ = ["ServiceAccounts", "AsyncServiceAccounts"] + + +class ServiceAccounts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ServiceAccountsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ServiceAccountsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ServiceAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ServiceAccountsWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountCreateResponse: + """Creates a new service account in the project. + + This also returns an unredacted + API key for the service account. + + Args: + name: The name of the service account being created. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + body=maybe_transform({"name": name}, service_account_create_params.ServiceAccountCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountCreateResponse, + ) + + def retrieve( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Retrieves a service account in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectServiceAccount]: + """ + Returns a list of service accounts in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + page=SyncConversationCursorPage[ProjectServiceAccount], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + service_account_list_params.ServiceAccountListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectServiceAccount, + ) + + def delete( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountDeleteResponse: + """ + Deletes a service account from the project. + + Returns confirmation of service account deletion, or an error if the project is + archived (archived projects have no service accounts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountDeleteResponse, + ) + + +class AsyncServiceAccounts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncServiceAccountsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncServiceAccountsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncServiceAccountsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncServiceAccountsWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + name: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountCreateResponse: + """Creates a new service account in the project. + + This also returns an unredacted + API key for the service account. + + Args: + name: The name of the service account being created. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + body=await async_maybe_transform({"name": name}, service_account_create_params.ServiceAccountCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountCreateResponse, + ) + + async def retrieve( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Retrieves a service account in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectServiceAccount, AsyncConversationCursorPage[ProjectServiceAccount]]: + """ + Returns a list of service accounts in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), + page=AsyncConversationCursorPage[ProjectServiceAccount], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + service_account_list_params.ServiceAccountListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectServiceAccount, + ) + + async def delete( + self, + service_account_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ServiceAccountDeleteResponse: + """ + Deletes a service account from the project. + + Returns confirmation of service account deletion, or an error if the project is + archived (archived projects have no service accounts). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ServiceAccountDeleteResponse, + ) + + +class ServiceAccountsWithRawResponse: + def __init__(self, service_accounts: ServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = _legacy_response.to_raw_response_wrapper( + service_accounts.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + service_accounts.retrieve, + ) + self.list = _legacy_response.to_raw_response_wrapper( + service_accounts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + service_accounts.delete, + ) + + +class AsyncServiceAccountsWithRawResponse: + def __init__(self, service_accounts: AsyncServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = _legacy_response.async_to_raw_response_wrapper( + service_accounts.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + service_accounts.retrieve, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + service_accounts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + service_accounts.delete, + ) + + +class ServiceAccountsWithStreamingResponse: + def __init__(self, service_accounts: ServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = to_streamed_response_wrapper( + service_accounts.create, + ) + self.retrieve = to_streamed_response_wrapper( + service_accounts.retrieve, + ) + self.list = to_streamed_response_wrapper( + service_accounts.list, + ) + self.delete = to_streamed_response_wrapper( + service_accounts.delete, + ) + + +class AsyncServiceAccountsWithStreamingResponse: + def __init__(self, service_accounts: AsyncServiceAccounts) -> None: + self._service_accounts = service_accounts + + self.create = async_to_streamed_response_wrapper( + service_accounts.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + service_accounts.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + service_accounts.list, + ) + self.delete = async_to_streamed_response_wrapper( + service_accounts.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/users/__init__.py b/src/openai/resources/admin/organization/projects/users/__init__.py new file mode 100644 index 0000000000..d230cb8f34 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/users/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) + +__all__ = [ + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", +] diff --git a/src/openai/resources/admin/organization/projects/users/roles.py b/src/openai/resources/admin/organization/projects/users/roles.py new file mode 100644 index 0000000000..35cde9b890 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/users/roles.py @@ -0,0 +1,426 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...... import _legacy_response +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncCursorPage, AsyncCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects.users import role_list_params, role_create_params +from ......types.admin.organization.projects.users.role_list_response import RoleListResponse +from ......types.admin.organization.projects.users.role_create_response import RoleCreateResponse +from ......types.admin.organization.projects.users.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + user_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a user within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + user_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[RoleListResponse]: + """ + Lists the project roles assigned to a user within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + page=SyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a user within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + user_id: str, + *, + project_id: str, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns a project role to a user within a project. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + user_id: str, + *, + project_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + """ + Lists the project roles assigned to a user within a project. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing project roles. + + limit: A limit on the number of project role assignments to return. + + order: Sort order for the returned project roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), + page=AsyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns a project role from a user within a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/users/users.py b/src/openai/resources/admin/organization/projects/users/users.py new file mode 100644 index 0000000000..024fc27043 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/users/users.py @@ -0,0 +1,659 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ...... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects import user_list_params, user_create_params, user_update_params +from ......types.admin.organization.projects.project_user import ProjectUser +from ......types.admin.organization.projects.user_delete_response import UserDeleteResponse + +__all__ = ["Users", "AsyncUsers"] + + +class Users(SyncAPIResource): + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> UsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsersWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + role: Literal["owner", "member"], + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """Adds a user to the project. + + Users must already be members of the organization to + be added to a project. + + Args: + role: `owner` or `member` + + user_id: The ID of the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + body=maybe_transform( + { + "role": role, + "user_id": user_id, + }, + user_create_params.UserCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def retrieve( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Retrieves a user in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def update( + self, + user_id: str, + *, + project_id: str, + role: Literal["owner", "member"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Modifies a user's role in the project. + + Args: + role: `owner` or `member` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + body=maybe_transform({"role": role}, user_update_params.UserUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectUser]: + """ + Returns a list of users in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + page=SyncConversationCursorPage[ProjectUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectUser, + ) + + def delete( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the project. + + Returns confirmation of project user deletion, or an error if the project is + archived (archived projects have no users). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class AsyncUsers(AsyncAPIResource): + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncUsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsersWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + role: Literal["owner", "member"], + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """Adds a user to the project. + + Users must already be members of the organization to + be added to a project. + + Args: + role: `owner` or `member` + + user_id: The ID of the user. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + body=await async_maybe_transform( + { + "role": role, + "user_id": user_id, + }, + user_create_params.UserCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + async def retrieve( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Retrieves a user in the project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + async def update( + self, + user_id: str, + *, + project_id: str, + role: Literal["owner", "member"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectUser: + """ + Modifies a user's role in the project. + + Args: + role: `owner` or `member` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + body=await async_maybe_transform({"role": role}, user_update_params.UserUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectUser, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectUser, AsyncConversationCursorPage[ProjectUser]]: + """ + Returns a list of users in the project. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/users", project_id=project_id), + page=AsyncConversationCursorPage[ProjectUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectUser, + ) + + async def delete( + self, + user_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the project. + + Returns confirmation of project user deletion, or an error if the project is + archived (archived projects have no users). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/users/{user_id}", project_id=project_id, user_id=user_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class UsersWithRawResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = _legacy_response.to_raw_response_wrapper( + users.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._users.roles) + + +class AsyncUsersWithRawResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = _legacy_response.async_to_raw_response_wrapper( + users.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._users.roles) + + +class UsersWithStreamingResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.create = to_streamed_response_wrapper( + users.create, + ) + self.retrieve = to_streamed_response_wrapper( + users.retrieve, + ) + self.update = to_streamed_response_wrapper( + users.update, + ) + self.list = to_streamed_response_wrapper( + users.list, + ) + self.delete = to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._users.roles) + + +class AsyncUsersWithStreamingResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.create = async_to_streamed_response_wrapper( + users.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + users.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + users.update, + ) + self.list = async_to_streamed_response_wrapper( + users.list, + ) + self.delete = async_to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._users.roles) diff --git a/src/openai/resources/admin/organization/roles.py b/src/openai/resources/admin/organization/roles.py new file mode 100644 index 0000000000..3bfb35fd73 --- /dev/null +++ b/src/openai/resources/admin/organization/roles.py @@ -0,0 +1,526 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import role_list_params, role_create_params, role_update_params +from ....types.admin.organization.role import Role +from ....types.admin.organization.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for the organization. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/roles", + body=maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def update( + self, + role_id: str, + *, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing organization role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._post( + path_template("/organization/roles/{role_id}", role_id=role_id), + body=maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[Role]: + """ + Lists the roles configured for the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/roles", + page=SyncCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + def delete( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + *, + permissions: SequenceNotStr[str], + role_name: str, + description: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Creates a custom role for the organization. + + Args: + permissions: Permissions to grant to the role. + + role_name: Unique name for the role. + + description: Optional description of the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/roles", + body=await async_maybe_transform( + { + "permissions": permissions, + "role_name": role_name, + "description": description, + }, + role_create_params.RoleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + async def update( + self, + role_id: str, + *, + description: Optional[str] | Omit = omit, + permissions: Optional[SequenceNotStr[str]] | Omit = omit, + role_name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Updates an existing organization role. + + Args: + description: New description for the role. + + permissions: Updated set of permissions for the role. + + role_name: New name for the role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._post( + path_template("/organization/roles/{role_id}", role_id=role_id), + body=await async_maybe_transform( + { + "description": description, + "permissions": permissions, + "role_name": role_name, + }, + role_update_params.RoleUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + + def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[Role, AsyncCursorPage[Role]]: + """ + Lists the roles configured for the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing roles. + + limit: A limit on the number of roles to return. Defaults to 1000. + + order: Sort order for the returned roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/roles", + page=AsyncCursorPage[Role], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=Role, + ) + + async def delete( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Deletes a custom role from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.update = _legacy_response.to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + roles.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.update = to_streamed_response_wrapper( + roles.update, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.update = async_to_streamed_response_wrapper( + roles.update, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/src/openai/resources/admin/organization/usage.py b/src/openai/resources/admin/organization/usage.py new file mode 100644 index 0000000000..2725d5e884 --- /dev/null +++ b/src/openai/resources/admin/organization/usage.py @@ -0,0 +1,1724 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.admin.organization import ( + usage_costs_params, + usage_images_params, + usage_embeddings_params, + usage_completions_params, + usage_moderations_params, + usage_vector_stores_params, + usage_audio_speeches_params, + usage_audio_transcriptions_params, + usage_code_interpreter_sessions_params, +) +from ....types.admin.organization.usage_costs_response import UsageCostsResponse +from ....types.admin.organization.usage_images_response import UsageImagesResponse +from ....types.admin.organization.usage_embeddings_response import UsageEmbeddingsResponse +from ....types.admin.organization.usage_completions_response import UsageCompletionsResponse +from ....types.admin.organization.usage_moderations_response import UsageModerationsResponse +from ....types.admin.organization.usage_vector_stores_response import UsageVectorStoresResponse +from ....types.admin.organization.usage_audio_speeches_response import UsageAudioSpeechesResponse +from ....types.admin.organization.usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse +from ....types.admin.organization.usage_code_interpreter_sessions_response import UsageCodeInterpreterSessionsResponse + +__all__ = ["Usage", "AsyncUsage"] + + +class Usage(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UsageWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsageWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsageWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsageWithStreamingResponse(self) + + def audio_speeches( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageAudioSpeechesResponse: + """ + Get audio speeches usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/audio_speeches", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_speeches_params.UsageAudioSpeechesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioSpeechesResponse, + ) + + def audio_transcriptions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageAudioTranscriptionsResponse: + """ + Get audio transcriptions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/audio_transcriptions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_transcriptions_params.UsageAudioTranscriptionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioTranscriptionsResponse, + ) + + def code_interpreter_sessions( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageCodeInterpreterSessionsResponse: + """ + Get code interpreter sessions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/code_interpreter_sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_code_interpreter_sessions_params.UsageCodeInterpreterSessionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCodeInterpreterSessionsResponse, + ) + + def completions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + batch: bool | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "batch", "service_tier"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageCompletionsResponse: + """ + Get completions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + batch: If `true`, return batch jobs only. If `false`, return non-batch jobs only. By + default, return both. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `batch`, `service_tier` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "batch": batch, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_completions_params.UsageCompletionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCompletionsResponse, + ) + + def costs( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "line_item", "api_key_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageCostsResponse: + """ + Get costs details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only costs for these API keys. + + bucket_width: Width of each time bucket in response. Currently only `1d` is supported, default + to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the costs by the specified fields. Support fields include `project_id`, + `line_item`, `api_key_id` and any combination of them. + + limit: A limit on the number of buckets to be returned. Limit can range between 1 and + 180, and the default is 7. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only costs for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/costs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_costs_params.UsageCostsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCostsResponse, + ) + + def embeddings( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageEmbeddingsResponse: + """ + Get embeddings usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/embeddings", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_embeddings_params.UsageEmbeddingsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageEmbeddingsResponse, + ) + + def images( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "size", "source"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + sizes: List[Literal["256x256", "512x512", "1024x1024", "1792x1792", "1024x1792"]] | Omit = omit, + sources: List[Literal["image.generation", "image.edit", "image.variation"]] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageImagesResponse: + """ + Get images usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `size`, `source` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + sizes: Return only usages for these image sizes. Possible values are `256x256`, + `512x512`, `1024x1024`, `1792x1792`, `1024x1792` or any combination of them. + + sources: Return only usages for these sources. Possible values are `image.generation`, + `image.edit`, `image.variation` or any combination of them. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/images", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "sizes": sizes, + "sources": sources, + "user_ids": user_ids, + }, + usage_images_params.UsageImagesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageImagesResponse, + ) + + def moderations( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageModerationsResponse: + """ + Get moderations usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/moderations", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_moderations_params.UsageModerationsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageModerationsResponse, + ) + + def vector_stores( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageVectorStoresResponse: + """ + Get vector stores usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/vector_stores", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_vector_stores_params.UsageVectorStoresParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageVectorStoresResponse, + ) + + +class AsyncUsage(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUsageWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsageWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsageWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsageWithStreamingResponse(self) + + async def audio_speeches( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageAudioSpeechesResponse: + """ + Get audio speeches usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/audio_speeches", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_speeches_params.UsageAudioSpeechesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioSpeechesResponse, + ) + + async def audio_transcriptions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageAudioTranscriptionsResponse: + """ + Get audio transcriptions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/audio_transcriptions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_audio_transcriptions_params.UsageAudioTranscriptionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageAudioTranscriptionsResponse, + ) + + async def code_interpreter_sessions( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageCodeInterpreterSessionsResponse: + """ + Get code interpreter sessions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/code_interpreter_sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_code_interpreter_sessions_params.UsageCodeInterpreterSessionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCodeInterpreterSessionsResponse, + ) + + async def completions( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + batch: bool | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "batch", "service_tier"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageCompletionsResponse: + """ + Get completions usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + batch: If `true`, return batch jobs only. If `false`, return non-batch jobs only. By + default, return both. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `batch`, `service_tier` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/completions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "batch": batch, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_completions_params.UsageCompletionsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCompletionsResponse, + ) + + async def costs( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "line_item", "api_key_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageCostsResponse: + """ + Get costs details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only costs for these API keys. + + bucket_width: Width of each time bucket in response. Currently only `1d` is supported, default + to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the costs by the specified fields. Support fields include `project_id`, + `line_item`, `api_key_id` and any combination of them. + + limit: A limit on the number of buckets to be returned. Limit can range between 1 and + 180, and the default is 7. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only costs for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/costs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_costs_params.UsageCostsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageCostsResponse, + ) + + async def embeddings( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageEmbeddingsResponse: + """ + Get embeddings usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/embeddings", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_embeddings_params.UsageEmbeddingsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageEmbeddingsResponse, + ) + + async def images( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "size", "source"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + sizes: List[Literal["256x256", "512x512", "1024x1024", "1792x1792", "1024x1792"]] | Omit = omit, + sources: List[Literal["image.generation", "image.edit", "image.variation"]] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageImagesResponse: + """ + Get images usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `size`, `source` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + sizes: Return only usages for these image sizes. Possible values are `256x256`, + `512x512`, `1024x1024`, `1792x1792`, `1024x1792` or any combination of them. + + sources: Return only usages for these sources. Possible values are `image.generation`, + `image.edit`, `image.variation` or any combination of them. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/images", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "sizes": sizes, + "sources": sources, + "user_ids": user_ids, + }, + usage_images_params.UsageImagesParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageImagesResponse, + ) + + async def moderations( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageModerationsResponse: + """ + Get moderations usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model` or any combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/moderations", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_moderations_params.UsageModerationsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageModerationsResponse, + ) + + async def vector_stores( + self, + *, + start_time: int, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageVectorStoresResponse: + """ + Get vector stores usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/vector_stores", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + }, + usage_vector_stores_params.UsageVectorStoresParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageVectorStoresResponse, + ) + + +class UsageWithRawResponse: + def __init__(self, usage: Usage) -> None: + self._usage = usage + + self.audio_speeches = _legacy_response.to_raw_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = _legacy_response.to_raw_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = _legacy_response.to_raw_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = _legacy_response.to_raw_response_wrapper( + usage.completions, + ) + self.costs = _legacy_response.to_raw_response_wrapper( + usage.costs, + ) + self.embeddings = _legacy_response.to_raw_response_wrapper( + usage.embeddings, + ) + self.images = _legacy_response.to_raw_response_wrapper( + usage.images, + ) + self.moderations = _legacy_response.to_raw_response_wrapper( + usage.moderations, + ) + self.vector_stores = _legacy_response.to_raw_response_wrapper( + usage.vector_stores, + ) + + +class AsyncUsageWithRawResponse: + def __init__(self, usage: AsyncUsage) -> None: + self._usage = usage + + self.audio_speeches = _legacy_response.async_to_raw_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = _legacy_response.async_to_raw_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = _legacy_response.async_to_raw_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = _legacy_response.async_to_raw_response_wrapper( + usage.completions, + ) + self.costs = _legacy_response.async_to_raw_response_wrapper( + usage.costs, + ) + self.embeddings = _legacy_response.async_to_raw_response_wrapper( + usage.embeddings, + ) + self.images = _legacy_response.async_to_raw_response_wrapper( + usage.images, + ) + self.moderations = _legacy_response.async_to_raw_response_wrapper( + usage.moderations, + ) + self.vector_stores = _legacy_response.async_to_raw_response_wrapper( + usage.vector_stores, + ) + + +class UsageWithStreamingResponse: + def __init__(self, usage: Usage) -> None: + self._usage = usage + + self.audio_speeches = to_streamed_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = to_streamed_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = to_streamed_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = to_streamed_response_wrapper( + usage.completions, + ) + self.costs = to_streamed_response_wrapper( + usage.costs, + ) + self.embeddings = to_streamed_response_wrapper( + usage.embeddings, + ) + self.images = to_streamed_response_wrapper( + usage.images, + ) + self.moderations = to_streamed_response_wrapper( + usage.moderations, + ) + self.vector_stores = to_streamed_response_wrapper( + usage.vector_stores, + ) + + +class AsyncUsageWithStreamingResponse: + def __init__(self, usage: AsyncUsage) -> None: + self._usage = usage + + self.audio_speeches = async_to_streamed_response_wrapper( + usage.audio_speeches, + ) + self.audio_transcriptions = async_to_streamed_response_wrapper( + usage.audio_transcriptions, + ) + self.code_interpreter_sessions = async_to_streamed_response_wrapper( + usage.code_interpreter_sessions, + ) + self.completions = async_to_streamed_response_wrapper( + usage.completions, + ) + self.costs = async_to_streamed_response_wrapper( + usage.costs, + ) + self.embeddings = async_to_streamed_response_wrapper( + usage.embeddings, + ) + self.images = async_to_streamed_response_wrapper( + usage.images, + ) + self.moderations = async_to_streamed_response_wrapper( + usage.moderations, + ) + self.vector_stores = async_to_streamed_response_wrapper( + usage.vector_stores, + ) diff --git a/src/openai/resources/admin/organization/users/__init__.py b/src/openai/resources/admin/organization/users/__init__.py new file mode 100644 index 0000000000..d230cb8f34 --- /dev/null +++ b/src/openai/resources/admin/organization/users/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from .users import ( + Users, + AsyncUsers, + UsersWithRawResponse, + AsyncUsersWithRawResponse, + UsersWithStreamingResponse, + AsyncUsersWithStreamingResponse, +) + +__all__ = [ + "Roles", + "AsyncRoles", + "RolesWithRawResponse", + "AsyncRolesWithRawResponse", + "RolesWithStreamingResponse", + "AsyncRolesWithStreamingResponse", + "Users", + "AsyncUsers", + "UsersWithRawResponse", + "AsyncUsersWithRawResponse", + "UsersWithStreamingResponse", + "AsyncUsersWithStreamingResponse", +] diff --git a/src/openai/resources/admin/organization/users/roles.py b/src/openai/resources/admin/organization/users/roles.py new file mode 100644 index 0000000000..4338d1ae1d --- /dev/null +++ b/src/openai/resources/admin/organization/users/roles.py @@ -0,0 +1,398 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncCursorPage, AsyncCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.users import role_list_params, role_create_params +from .....types.admin.organization.users.role_list_response import RoleListResponse +from .....types.admin.organization.users.role_create_response import RoleCreateResponse +from .....types.admin.organization.users.role_delete_response import RoleDeleteResponse + +__all__ = ["Roles", "AsyncRoles"] + + +class Roles(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return RolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return RolesWithStreamingResponse(self) + + def create( + self, + user_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a user within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + body=maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + user_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[RoleListResponse]: + """ + Lists the organization roles assigned to a user within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + page=SyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + def delete( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a user within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._delete( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class AsyncRoles(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRolesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncRolesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRolesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncRolesWithStreamingResponse(self) + + async def create( + self, + user_id: str, + *, + role_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleCreateResponse: + """ + Assigns an organization role to a user within the organization. + + Args: + role_id: Identifier of the role to assign. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + body=await async_maybe_transform({"role_id": role_id}, role_create_params.RoleCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleCreateResponse, + ) + + def list( + self, + user_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + """ + Lists the organization roles assigned to a user within the organization. + + Args: + after: Cursor for pagination. Provide the value from the previous response's `next` + field to continue listing organization roles. + + limit: A limit on the number of organization role assignments to return. + + order: Sort order for the returned organization roles. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get_api_list( + path_template("/organization/users/{user_id}/roles", user_id=user_id), + page=AsyncCursorPage[RoleListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "limit": limit, + "order": order, + }, + role_list_params.RoleListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=RoleListResponse, + ) + + async def delete( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleDeleteResponse: + """ + Unassigns an organization role from a user within the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._delete( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleDeleteResponse, + ) + + +class RolesWithRawResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = _legacy_response.to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithRawResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = _legacy_response.async_to_raw_response_wrapper( + roles.create, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + roles.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + roles.delete, + ) + + +class RolesWithStreamingResponse: + def __init__(self, roles: Roles) -> None: + self._roles = roles + + self.create = to_streamed_response_wrapper( + roles.create, + ) + self.list = to_streamed_response_wrapper( + roles.list, + ) + self.delete = to_streamed_response_wrapper( + roles.delete, + ) + + +class AsyncRolesWithStreamingResponse: + def __init__(self, roles: AsyncRoles) -> None: + self._roles = roles + + self.create = async_to_streamed_response_wrapper( + roles.create, + ) + self.list = async_to_streamed_response_wrapper( + roles.list, + ) + self.delete = async_to_streamed_response_wrapper( + roles.delete, + ) diff --git a/src/openai/resources/admin/organization/users/users.py b/src/openai/resources/admin/organization/users/users.py new file mode 100644 index 0000000000..bfab2c5ecd --- /dev/null +++ b/src/openai/resources/admin/organization/users/users.py @@ -0,0 +1,509 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from .roles import ( + Roles, + AsyncRoles, + RolesWithRawResponse, + AsyncRolesWithRawResponse, + RolesWithStreamingResponse, + AsyncRolesWithStreamingResponse, +) +from ....._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization import user_list_params, user_update_params +from .....types.admin.organization.organization_user import OrganizationUser +from .....types.admin.organization.user_delete_response import UserDeleteResponse + +__all__ = ["Users", "AsyncUsers"] + + +class Users(SyncAPIResource): + @cached_property + def roles(self) -> Roles: + return Roles(self._client) + + @cached_property + def with_raw_response(self) -> UsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return UsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return UsersWithStreamingResponse(self) + + def retrieve( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Retrieves a user by their identifier. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + def update( + self, + user_id: str, + *, + role: Literal["owner", "reader"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Modifies a user's role in the organization. + + Args: + role: `owner` or `reader` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._post( + path_template("/organization/users/{user_id}", user_id=user_id), + body=maybe_transform({"role": role}, user_update_params.UserUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + def list( + self, + *, + after: str | Omit = omit, + emails: SequenceNotStr[str] | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[OrganizationUser]: + """ + Lists all of the users in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + emails: Filter by the email address of users. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/users", + page=SyncConversationCursorPage[OrganizationUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "emails": emails, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationUser, + ) + + def delete( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._delete( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class AsyncUsers(AsyncAPIResource): + @cached_property + def roles(self) -> AsyncRoles: + return AsyncRoles(self._client) + + @cached_property + def with_raw_response(self) -> AsyncUsersWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsersWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsersWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncUsersWithStreamingResponse(self) + + async def retrieve( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Retrieves a user by their identifier. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._get( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + async def update( + self, + user_id: str, + *, + role: Literal["owner", "reader"], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationUser: + """ + Modifies a user's role in the organization. + + Args: + role: `owner` or `reader` + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._post( + path_template("/organization/users/{user_id}", user_id=user_id), + body=await async_maybe_transform({"role": role}, user_update_params.UserUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationUser, + ) + + def list( + self, + *, + after: str | Omit = omit, + emails: SequenceNotStr[str] | Omit = omit, + limit: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OrganizationUser, AsyncConversationCursorPage[OrganizationUser]]: + """ + Lists all of the users in the organization. + + Args: + after: A cursor for use in pagination. `after` is an object ID that defines your place + in the list. For instance, if you make a list request and receive 100 objects, + ending with obj_foo, your subsequent call can include after=obj_foo in order to + fetch the next page of the list. + + emails: Filter by the email address of users. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/users", + page=AsyncConversationCursorPage[OrganizationUser], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "emails": emails, + "limit": limit, + }, + user_list_params.UserListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationUser, + ) + + async def delete( + self, + user_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserDeleteResponse: + """ + Deletes a user from the organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._delete( + path_template("/organization/users/{user_id}", user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserDeleteResponse, + ) + + +class UsersWithRawResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.retrieve = _legacy_response.to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithRawResponse: + return RolesWithRawResponse(self._users.roles) + + +class AsyncUsersWithRawResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + users.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + users.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + users.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithRawResponse: + return AsyncRolesWithRawResponse(self._users.roles) + + +class UsersWithStreamingResponse: + def __init__(self, users: Users) -> None: + self._users = users + + self.retrieve = to_streamed_response_wrapper( + users.retrieve, + ) + self.update = to_streamed_response_wrapper( + users.update, + ) + self.list = to_streamed_response_wrapper( + users.list, + ) + self.delete = to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> RolesWithStreamingResponse: + return RolesWithStreamingResponse(self._users.roles) + + +class AsyncUsersWithStreamingResponse: + def __init__(self, users: AsyncUsers) -> None: + self._users = users + + self.retrieve = async_to_streamed_response_wrapper( + users.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + users.update, + ) + self.list = async_to_streamed_response_wrapper( + users.list, + ) + self.delete = async_to_streamed_response_wrapper( + users.delete, + ) + + @cached_property + def roles(self) -> AsyncRolesWithStreamingResponse: + return AsyncRolesWithStreamingResponse(self._users.roles) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index c402a94c9d..3fbd5cf1ac 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -2,5 +2,62 @@ from __future__ import annotations +from .role import Role as Role +from .group import Group as Group +from .invite import Invite as Invite +from .project import Project as Project +from .certificate import Certificate as Certificate +from .admin_api_key import AdminAPIKey as AdminAPIKey +from .role_list_params import RoleListParams as RoleListParams +from .user_list_params import UserListParams as UserListParams +from .group_list_params import GroupListParams as GroupListParams +from .organization_user import OrganizationUser as OrganizationUser +from .invite_list_params import InviteListParams as InviteListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_update_params import RoleUpdateParams as RoleUpdateParams +from .usage_costs_params import UsageCostsParams as UsageCostsParams +from .user_update_params import UserUpdateParams as UserUpdateParams +from .group_create_params import GroupCreateParams as GroupCreateParams +from .group_update_params import GroupUpdateParams as GroupUpdateParams +from .project_list_params import ProjectListParams as ProjectListParams +from .usage_images_params import UsageImagesParams as UsageImagesParams +from .invite_create_params import InviteCreateParams as InviteCreateParams +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .usage_costs_response import UsageCostsResponse as UsageCostsResponse +from .user_delete_response import UserDeleteResponse as UserDeleteResponse from .audit_log_list_params import AuditLogListParams as AuditLogListParams +from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse +from .group_update_response import GroupUpdateResponse as GroupUpdateResponse +from .project_create_params import ProjectCreateParams as ProjectCreateParams +from .project_update_params import ProjectUpdateParams as ProjectUpdateParams +from .usage_images_response import UsageImagesResponse as UsageImagesResponse +from .invite_delete_response import InviteDeleteResponse as InviteDeleteResponse from .audit_log_list_response import AuditLogListResponse as AuditLogListResponse +from .certificate_list_params import CertificateListParams as CertificateListParams +from .usage_embeddings_params import UsageEmbeddingsParams as UsageEmbeddingsParams +from .usage_completions_params import UsageCompletionsParams as UsageCompletionsParams +from .usage_moderations_params import UsageModerationsParams as UsageModerationsParams +from .admin_api_key_list_params import AdminAPIKeyListParams as AdminAPIKeyListParams +from .certificate_create_params import CertificateCreateParams as CertificateCreateParams +from .certificate_update_params import CertificateUpdateParams as CertificateUpdateParams +from .usage_embeddings_response import UsageEmbeddingsResponse as UsageEmbeddingsResponse +from .usage_completions_response import UsageCompletionsResponse as UsageCompletionsResponse +from .usage_moderations_response import UsageModerationsResponse as UsageModerationsResponse +from .usage_vector_stores_params import UsageVectorStoresParams as UsageVectorStoresParams +from .admin_api_key_create_params import AdminAPIKeyCreateParams as AdminAPIKeyCreateParams +from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams +from .certificate_delete_response import CertificateDeleteResponse as CertificateDeleteResponse +from .certificate_retrieve_params import CertificateRetrieveParams as CertificateRetrieveParams +from .usage_audio_speeches_params import UsageAudioSpeechesParams as UsageAudioSpeechesParams +from .usage_vector_stores_response import UsageVectorStoresResponse as UsageVectorStoresResponse +from .admin_api_key_delete_response import AdminAPIKeyDeleteResponse as AdminAPIKeyDeleteResponse +from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams +from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse +from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams +from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse +from .usage_code_interpreter_sessions_params import ( + UsageCodeInterpreterSessionsParams as UsageCodeInterpreterSessionsParams, +) +from .usage_code_interpreter_sessions_response import ( + UsageCodeInterpreterSessionsResponse as UsageCodeInterpreterSessionsResponse, +) diff --git a/src/openai/types/admin/organization/admin_api_key.py b/src/openai/types/admin/organization/admin_api_key.py new file mode 100644 index 0000000000..576d591d95 --- /dev/null +++ b/src/openai/types/admin/organization/admin_api_key.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["AdminAPIKey", "Owner"] + + +class Owner(BaseModel): + id: Optional[str] = None + """The identifier, which can be referenced in API endpoints""" + + created_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the user was created""" + + name: Optional[str] = None + """The name of the user""" + + object: Optional[str] = None + """The object type, which is always organization.user""" + + role: Optional[str] = None + """Always `owner`""" + + type: Optional[str] = None + """Always `user`""" + + +class AdminAPIKey(BaseModel): + """Represents an individual Admin API key in an org.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the API key was created""" + + last_used_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the API key was last used""" + + name: str + """The name of the API key""" + + object: str + """The object type, which is always `organization.admin_api_key`""" + + owner: Owner + + redacted_value: str + """The redacted value of the API key""" + + value: Optional[str] = None + """The value of the API key. Only shown on create.""" diff --git a/src/openai/types/admin/organization/admin_api_key_create_params.py b/src/openai/types/admin/organization/admin_api_key_create_params.py new file mode 100644 index 0000000000..dccdfb8a75 --- /dev/null +++ b/src/openai/types/admin/organization/admin_api_key_create_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["AdminAPIKeyCreateParams"] + + +class AdminAPIKeyCreateParams(TypedDict, total=False): + name: Required[str] diff --git a/src/openai/types/admin/organization/admin_api_key_delete_response.py b/src/openai/types/admin/organization/admin_api_key_delete_response.py new file mode 100644 index 0000000000..7b4dab86a1 --- /dev/null +++ b/src/openai/types/admin/organization/admin_api_key_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ...._models import BaseModel + +__all__ = ["AdminAPIKeyDeleteResponse"] + + +class AdminAPIKeyDeleteResponse(BaseModel): + id: Optional[str] = None + + deleted: Optional[bool] = None + + object: Optional[str] = None diff --git a/src/openai/types/admin/organization/admin_api_key_list_params.py b/src/openai/types/admin/organization/admin_api_key_list_params.py new file mode 100644 index 0000000000..c3b3f51008 --- /dev/null +++ b/src/openai/types/admin/organization/admin_api_key_list_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +__all__ = ["AdminAPIKeyListParams"] + + +class AdminAPIKeyListParams(TypedDict, total=False): + after: Optional[str] + """Return keys with IDs that come after this ID in the pagination order.""" + + limit: int + """Maximum number of keys to return.""" + + order: Literal["asc", "desc"] + """Order results by creation time, ascending or descending.""" diff --git a/src/openai/types/admin/organization/certificate.py b/src/openai/types/admin/organization/certificate.py new file mode 100644 index 0000000000..93347d816b --- /dev/null +++ b/src/openai/types/admin/organization/certificate.py @@ -0,0 +1,51 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Certificate", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + content: Optional[str] = None + """The content of the certificate in PEM format.""" + + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class Certificate(BaseModel): + """Represents an individual `certificate` uploaded to the organization.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: str + """The name of the certificate.""" + + object: Literal["certificate", "organization.certificate", "organization.project.certificate"] + """The object type. + + - If creating, updating, or getting a specific certificate, the object type is + `certificate`. + - If listing, activating, or deactivating certificates for the organization, the + object type is `organization.certificate`. + - If listing, activating, or deactivating certificates for a project, the object + type is `organization.project.certificate`. + """ + + active: Optional[bool] = None + """Whether the certificate is currently active at the specified scope. + + Not returned when getting details for a specific certificate. + """ diff --git a/src/openai/types/admin/organization/certificate_activate_params.py b/src/openai/types/admin/organization/certificate_activate_params.py new file mode 100644 index 0000000000..0bb6474c7c --- /dev/null +++ b/src/openai/types/admin/organization/certificate_activate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["CertificateActivateParams"] + + +class CertificateActivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/src/openai/types/admin/organization/certificate_create_params.py b/src/openai/types/admin/organization/certificate_create_params.py new file mode 100644 index 0000000000..3af81f03cb --- /dev/null +++ b/src/openai/types/admin/organization/certificate_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CertificateCreateParams"] + + +class CertificateCreateParams(TypedDict, total=False): + content: Required[str] + """The certificate content in PEM format""" + + name: str + """An optional name for the certificate""" diff --git a/src/openai/types/admin/organization/certificate_deactivate_params.py b/src/openai/types/admin/organization/certificate_deactivate_params.py new file mode 100644 index 0000000000..827af54d35 --- /dev/null +++ b/src/openai/types/admin/organization/certificate_deactivate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["CertificateDeactivateParams"] + + +class CertificateDeactivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/src/openai/types/admin/organization/certificate_delete_response.py b/src/openai/types/admin/organization/certificate_delete_response.py new file mode 100644 index 0000000000..b0dc1fa018 --- /dev/null +++ b/src/openai/types/admin/organization/certificate_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateDeleteResponse"] + + +class CertificateDeleteResponse(BaseModel): + id: str + """The ID of the certificate that was deleted.""" + + object: Literal["certificate.deleted"] + """The object type, must be `certificate.deleted`.""" diff --git a/src/openai/types/admin/organization/certificate_list_params.py b/src/openai/types/admin/organization/certificate_list_params.py new file mode 100644 index 0000000000..1d9aff4b4a --- /dev/null +++ b/src/openai/types/admin/organization/certificate_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["CertificateListParams"] + + +class CertificateListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/admin/organization/certificate_retrieve_params.py b/src/openai/types/admin/organization/certificate_retrieve_params.py new file mode 100644 index 0000000000..29bc8dedc5 --- /dev/null +++ b/src/openai/types/admin/organization/certificate_retrieve_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, TypedDict + +__all__ = ["CertificateRetrieveParams"] + + +class CertificateRetrieveParams(TypedDict, total=False): + include: List[Literal["content"]] + """A list of additional fields to include in the response. + + Currently the only supported value is `content` to fetch the PEM content of the + certificate. + """ diff --git a/src/openai/types/admin/organization/certificate_update_params.py b/src/openai/types/admin/organization/certificate_update_params.py new file mode 100644 index 0000000000..8a048b52b9 --- /dev/null +++ b/src/openai/types/admin/organization/certificate_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["CertificateUpdateParams"] + + +class CertificateUpdateParams(TypedDict, total=False): + name: Required[str] + """The updated name for the certificate""" diff --git a/src/openai/types/admin/organization/group.py b/src/openai/types/admin/organization/group.py new file mode 100644 index 0000000000..ce3b0d41b3 --- /dev/null +++ b/src/openai/types/admin/organization/group.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["Group"] + + +class Group(BaseModel): + """Details about an organization group.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + is_scim_managed: bool + """ + Whether the group is managed through SCIM and controlled by your identity + provider. + """ + + name: str + """Display name of the group.""" diff --git a/src/openai/types/admin/organization/group_create_params.py b/src/openai/types/admin/organization/group_create_params.py new file mode 100644 index 0000000000..8e27d299d3 --- /dev/null +++ b/src/openai/types/admin/organization/group_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GroupCreateParams"] + + +class GroupCreateParams(TypedDict, total=False): + name: Required[str] + """Human readable name for the group.""" diff --git a/src/openai/types/admin/organization/group_delete_response.py b/src/openai/types/admin/organization/group_delete_response.py new file mode 100644 index 0000000000..6dec56e58d --- /dev/null +++ b/src/openai/types/admin/organization/group_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["GroupDeleteResponse"] + + +class GroupDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a group.""" + + id: str + """Identifier of the deleted group.""" + + deleted: bool + """Whether the group was deleted.""" + + object: Literal["group.deleted"] + """Always `group.deleted`.""" diff --git a/src/openai/types/admin/organization/group_list_params.py b/src/openai/types/admin/organization/group_list_params.py new file mode 100644 index 0000000000..198478b35a --- /dev/null +++ b/src/openai/types/admin/organization/group_list_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["GroupListParams"] + + +class GroupListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is a group ID that defines your place in the list. For instance, if you + make a list request and receive 100 objects, ending with group_abc, your + subsequent call can include `after=group_abc` in order to fetch the next page of + the list. + """ + + limit: int + """A limit on the number of groups to be returned. + + Limit can range between 0 and 1000, and the default is 100. + """ + + order: Literal["asc", "desc"] + """Specifies the sort order of the returned groups.""" diff --git a/src/openai/types/admin/organization/group_update_params.py b/src/openai/types/admin/organization/group_update_params.py new file mode 100644 index 0000000000..2bb3a9d8fe --- /dev/null +++ b/src/openai/types/admin/organization/group_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GroupUpdateParams"] + + +class GroupUpdateParams(TypedDict, total=False): + name: Required[str] + """New display name for the group.""" diff --git a/src/openai/types/admin/organization/group_update_response.py b/src/openai/types/admin/organization/group_update_response.py new file mode 100644 index 0000000000..1ae6f86a64 --- /dev/null +++ b/src/openai/types/admin/organization/group_update_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ...._models import BaseModel + +__all__ = ["GroupUpdateResponse"] + + +class GroupUpdateResponse(BaseModel): + """Response returned after updating a group.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + is_scim_managed: bool + """ + Whether the group is managed through SCIM and controlled by your identity + provider. + """ + + name: str + """Updated display name for the group.""" diff --git a/src/openai/types/admin/organization/groups/__init__.py b/src/openai/types/admin/organization/groups/__init__.py new file mode 100644 index 0000000000..a1af586634 --- /dev/null +++ b/src/openai/types/admin/organization/groups/__init__.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .user_list_params import UserListParams as UserListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .user_create_params import UserCreateParams as UserCreateParams +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .user_create_response import UserCreateResponse as UserCreateResponse +from .user_delete_response import UserDeleteResponse as UserDeleteResponse diff --git a/src/openai/types/admin/organization/groups/role_create_params.py b/src/openai/types/admin/organization/groups/role_create_params.py new file mode 100644 index 0000000000..0ebc196eef --- /dev/null +++ b/src/openai/types/admin/organization/groups/role_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/src/openai/types/admin/organization/groups/role_create_response.py b/src/openai/types/admin/organization/groups/role_create_response.py new file mode 100644 index 0000000000..8f82bfc542 --- /dev/null +++ b/src/openai/types/admin/organization/groups/role_create_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..role import Role +from ....._models import BaseModel + +__all__ = ["RoleCreateResponse", "Group"] + + +class Group(BaseModel): + """Summary information about a group returned in role assignment responses.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + name: str + """Display name of the group.""" + + object: Literal["group"] + """Always `group`.""" + + scim_managed: bool + """Whether the group is managed through SCIM.""" + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a group to a role.""" + + group: Group + """Summary information about a group returned in role assignment responses.""" + + object: Literal["group.role"] + """Always `group.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" diff --git a/src/openai/types/admin/organization/groups/role_delete_response.py b/src/openai/types/admin/organization/groups/role_delete_response.py new file mode 100644 index 0000000000..fb6a111614 --- /dev/null +++ b/src/openai/types/admin/organization/groups/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ....._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/src/openai/types/admin/organization/groups/role_list_params.py b/src/openai/types/admin/organization/groups/role_list_params.py new file mode 100644 index 0000000000..451a1a2045 --- /dev/null +++ b/src/openai/types/admin/organization/groups/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + organization roles. + """ + + limit: int + """A limit on the number of organization role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned organization roles.""" diff --git a/src/openai/types/admin/organization/groups/role_list_response.py b/src/openai/types/admin/organization/groups/role_list_response.py new file mode 100644 index 0000000000..337d517ba1 --- /dev/null +++ b/src/openai/types/admin/organization/groups/role_list_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleListResponse"] + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/groups/user_create_params.py b/src/openai/types/admin/organization/groups/user_create_params.py new file mode 100644 index 0000000000..ec30b46f1c --- /dev/null +++ b/src/openai/types/admin/organization/groups/user_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["UserCreateParams"] + + +class UserCreateParams(TypedDict, total=False): + user_id: Required[str] + """Identifier of the user to add to the group.""" diff --git a/src/openai/types/admin/organization/groups/user_create_response.py b/src/openai/types/admin/organization/groups/user_create_response.py new file mode 100644 index 0000000000..508f51747e --- /dev/null +++ b/src/openai/types/admin/organization/groups/user_create_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserCreateResponse"] + + +class UserCreateResponse(BaseModel): + """Confirmation payload returned after adding a user to a group.""" + + group_id: str + """Identifier of the group the user was added to.""" + + object: Literal["group.user"] + """Always `group.user`.""" + + user_id: str + """Identifier of the user that was added.""" diff --git a/src/openai/types/admin/organization/groups/user_delete_response.py b/src/openai/types/admin/organization/groups/user_delete_response.py new file mode 100644 index 0000000000..3b484a9baf --- /dev/null +++ b/src/openai/types/admin/organization/groups/user_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserDeleteResponse"] + + +class UserDeleteResponse(BaseModel): + """Confirmation payload returned after removing a user from a group.""" + + deleted: bool + """Whether the group membership was removed.""" + + object: Literal["group.user.deleted"] + """Always `group.user.deleted`.""" diff --git a/src/openai/types/admin/organization/groups/user_list_params.py b/src/openai/types/admin/organization/groups/user_list_params.py new file mode 100644 index 0000000000..09bcfb1ba7 --- /dev/null +++ b/src/openai/types/admin/organization/groups/user_list_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["UserListParams"] + + +class UserListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + Provide the ID of the last user from the previous list response to retrieve the + next page. + """ + + limit: int + """A limit on the number of users to be returned. + + Limit can range between 0 and 1000, and the default is 100. + """ + + order: Literal["asc", "desc"] + """Specifies the sort order of users in the list.""" diff --git a/src/openai/types/admin/organization/invite.py b/src/openai/types/admin/organization/invite.py new file mode 100644 index 0000000000..5c051500b9 --- /dev/null +++ b/src/openai/types/admin/organization/invite.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Invite", "Project"] + + +class Project(BaseModel): + id: Optional[str] = None + """Project's public ID""" + + role: Optional[Literal["member", "owner"]] = None + """Project membership role""" + + +class Invite(BaseModel): + """Represents an individual `invite` to the organization.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + email: str + """The email address of the individual to whom the invite was sent""" + + expires_at: int + """The Unix timestamp (in seconds) of when the invite expires.""" + + invited_at: int + """The Unix timestamp (in seconds) of when the invite was sent.""" + + object: Literal["organization.invite"] + """The object type, which is always `organization.invite`""" + + role: Literal["owner", "reader"] + """`owner` or `reader`""" + + status: Literal["accepted", "expired", "pending"] + """`accepted`,`expired`, or `pending`""" + + accepted_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the invite was accepted.""" + + projects: Optional[List[Project]] = None + """The projects that were granted membership upon acceptance of the invite.""" diff --git a/src/openai/types/admin/organization/invite_create_params.py b/src/openai/types/admin/organization/invite_create_params.py new file mode 100644 index 0000000000..7709003fe3 --- /dev/null +++ b/src/openai/types/admin/organization/invite_create_params.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["InviteCreateParams", "Project"] + + +class InviteCreateParams(TypedDict, total=False): + email: Required[str] + """Send an email to this address""" + + role: Required[Literal["reader", "owner"]] + """`owner` or `reader`""" + + projects: Iterable[Project] + """ + An array of projects to which membership is granted at the same time the org + invite is accepted. If omitted, the user will be invited to the default project + for compatibility with legacy behavior. + """ + + +class Project(TypedDict, total=False): + id: Required[str] + """Project's public ID""" + + role: Required[Literal["member", "owner"]] + """Project membership role""" diff --git a/src/openai/types/admin/organization/invite_delete_response.py b/src/openai/types/admin/organization/invite_delete_response.py new file mode 100644 index 0000000000..1a8aa0ce2f --- /dev/null +++ b/src/openai/types/admin/organization/invite_delete_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InviteDeleteResponse"] + + +class InviteDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.invite.deleted"] + """The object type, which is always `organization.invite.deleted`""" diff --git a/src/openai/types/admin/organization/invite_list_params.py b/src/openai/types/admin/organization/invite_list_params.py new file mode 100644 index 0000000000..678510d655 --- /dev/null +++ b/src/openai/types/admin/organization/invite_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["InviteListParams"] + + +class InviteListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/src/openai/types/admin/organization/organization_user.py b/src/openai/types/admin/organization/organization_user.py new file mode 100644 index 0000000000..3ffe572465 --- /dev/null +++ b/src/openai/types/admin/organization/organization_user.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationUser"] + + +class OrganizationUser(BaseModel): + """Represents an individual `user` within an organization.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + added_at: int + """The Unix timestamp (in seconds) of when the user was added.""" + + email: str + """The email address of the user""" + + name: str + """The name of the user""" + + object: Literal["organization.user"] + """The object type, which is always `organization.user`""" + + role: Literal["owner", "reader"] + """`owner` or `reader`""" diff --git a/src/openai/types/admin/organization/project.py b/src/openai/types/admin/organization/project.py new file mode 100644 index 0000000000..a1058af3d5 --- /dev/null +++ b/src/openai/types/admin/organization/project.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Project"] + + +class Project(BaseModel): + """Represents an individual project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the project was created.""" + + name: str + """The name of the project. This appears in reporting.""" + + object: Literal["organization.project"] + """The object type, which is always `organization.project`""" + + status: Literal["active", "archived"] + """`active` or `archived`""" + + archived_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the project was archived or `null`.""" diff --git a/src/openai/types/admin/organization/project_create_params.py b/src/openai/types/admin/organization/project_create_params.py new file mode 100644 index 0000000000..8b8d1b44e8 --- /dev/null +++ b/src/openai/types/admin/organization/project_create_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ProjectCreateParams"] + + +class ProjectCreateParams(TypedDict, total=False): + name: Required[str] + """The friendly name of the project, this name appears in reports.""" + + geography: Literal["US", "EU", "JP", "IN", "KR", "CA", "AU", "SG"] + """Create the project with the specified data residency region. + + Your organization must have access to Data residency functionality in order to + use. See + [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) + to review the functionality and limitations of setting this field. + """ diff --git a/src/openai/types/admin/organization/project_list_params.py b/src/openai/types/admin/organization/project_list_params.py new file mode 100644 index 0000000000..f55fb8a392 --- /dev/null +++ b/src/openai/types/admin/organization/project_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectListParams"] + + +class ProjectListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + include_archived: bool + """If `true` returns all projects including those that have been `archived`. + + Archived projects are not included by default. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/src/openai/types/admin/organization/project_update_params.py b/src/openai/types/admin/organization/project_update_params.py new file mode 100644 index 0000000000..0ba1984a9f --- /dev/null +++ b/src/openai/types/admin/organization/project_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ProjectUpdateParams"] + + +class ProjectUpdateParams(TypedDict, total=False): + name: Required[str] + """The updated name of the project, this name appears in reports.""" diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py new file mode 100644 index 0000000000..8af54d8659 --- /dev/null +++ b/src/openai/types/admin/organization/projects/__init__.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .project_user import ProjectUser as ProjectUser +from .project_group import ProjectGroup as ProjectGroup +from .project_api_key import ProjectAPIKey as ProjectAPIKey +from .role_list_params import RoleListParams as RoleListParams +from .user_list_params import UserListParams as UserListParams +from .group_list_params import GroupListParams as GroupListParams +from .project_rate_limit import ProjectRateLimit as ProjectRateLimit +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_update_params import RoleUpdateParams as RoleUpdateParams +from .user_create_params import UserCreateParams as UserCreateParams +from .user_update_params import UserUpdateParams as UserUpdateParams +from .api_key_list_params import APIKeyListParams as APIKeyListParams +from .group_create_params import GroupCreateParams as GroupCreateParams +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .user_delete_response import UserDeleteResponse as UserDeleteResponse +from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse +from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse +from .certificate_list_params import CertificateListParams as CertificateListParams +from .project_service_account import ProjectServiceAccount as ProjectServiceAccount +from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams +from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams +from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams +from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams +from .service_account_create_response import ServiceAccountCreateResponse as ServiceAccountCreateResponse +from .service_account_delete_response import ServiceAccountDeleteResponse as ServiceAccountDeleteResponse +from .rate_limit_list_rate_limits_params import RateLimitListRateLimitsParams as RateLimitListRateLimitsParams +from .rate_limit_update_rate_limit_params import RateLimitUpdateRateLimitParams as RateLimitUpdateRateLimitParams diff --git a/src/openai/types/admin/organization/projects/api_key_delete_response.py b/src/openai/types/admin/organization/projects/api_key_delete_response.py new file mode 100644 index 0000000000..253a6746ba --- /dev/null +++ b/src/openai/types/admin/organization/projects/api_key_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["APIKeyDeleteResponse"] + + +class APIKeyDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.project.api_key.deleted"] diff --git a/src/openai/types/admin/organization/projects/api_key_list_params.py b/src/openai/types/admin/organization/projects/api_key_list_params.py new file mode 100644 index 0000000000..422a28518e --- /dev/null +++ b/src/openai/types/admin/organization/projects/api_key_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["APIKeyListParams"] + + +class APIKeyListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/src/openai/types/admin/organization/projects/certificate_activate_params.py b/src/openai/types/admin/organization/projects/certificate_activate_params.py new file mode 100644 index 0000000000..a0e7cd20e8 --- /dev/null +++ b/src/openai/types/admin/organization/projects/certificate_activate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["CertificateActivateParams"] + + +class CertificateActivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/src/openai/types/admin/organization/projects/certificate_deactivate_params.py b/src/openai/types/admin/organization/projects/certificate_deactivate_params.py new file mode 100644 index 0000000000..75c32708ba --- /dev/null +++ b/src/openai/types/admin/organization/projects/certificate_deactivate_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["CertificateDeactivateParams"] + + +class CertificateDeactivateParams(TypedDict, total=False): + certificate_ids: Required[SequenceNotStr[str]] diff --git a/src/openai/types/admin/organization/projects/certificate_list_params.py b/src/openai/types/admin/organization/projects/certificate_list_params.py new file mode 100644 index 0000000000..1d9aff4b4a --- /dev/null +++ b/src/openai/types/admin/organization/projects/certificate_list_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["CertificateListParams"] + + +class CertificateListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """Sort order by the `created_at` timestamp of the objects. + + `asc` for ascending order and `desc` for descending order. + """ diff --git a/src/openai/types/admin/organization/projects/group_create_params.py b/src/openai/types/admin/organization/projects/group_create_params.py new file mode 100644 index 0000000000..b9f4626d74 --- /dev/null +++ b/src/openai/types/admin/organization/projects/group_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["GroupCreateParams"] + + +class GroupCreateParams(TypedDict, total=False): + group_id: Required[str] + """Identifier of the group to add to the project.""" + + role: Required[str] + """Identifier of the project role to grant to the group.""" diff --git a/src/openai/types/admin/organization/projects/group_delete_response.py b/src/openai/types/admin/organization/projects/group_delete_response.py new file mode 100644 index 0000000000..ef1ce0ddb8 --- /dev/null +++ b/src/openai/types/admin/organization/projects/group_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["GroupDeleteResponse"] + + +class GroupDeleteResponse(BaseModel): + """Confirmation payload returned after removing a group from a project.""" + + deleted: bool + """Whether the group membership in the project was removed.""" + + object: Literal["project.group.deleted"] + """Always `project.group.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/group_list_params.py b/src/openai/types/admin/organization/projects/group_list_params.py new file mode 100644 index 0000000000..26ab31a88b --- /dev/null +++ b/src/openai/types/admin/organization/projects/group_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["GroupListParams"] + + +class GroupListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last group from the previous response to fetch the next + page. + """ + + limit: int + """A limit on the number of project groups to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned groups.""" diff --git a/src/openai/types/admin/organization/projects/groups/__init__.py b/src/openai/types/admin/organization/projects/groups/__init__.py new file mode 100644 index 0000000000..ed464fde83 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse diff --git a/src/openai/types/admin/organization/projects/groups/role_create_params.py b/src/openai/types/admin/organization/projects/groups/role_create_params.py new file mode 100644 index 0000000000..2aba01e8a4 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/role_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + project_id: Required[str] + + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/src/openai/types/admin/organization/projects/groups/role_create_response.py b/src/openai/types/admin/organization/projects/groups/role_create_response.py new file mode 100644 index 0000000000..c6e7a1c048 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/role_create_response.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...role import Role +from ......_models import BaseModel + +__all__ = ["RoleCreateResponse", "Group"] + + +class Group(BaseModel): + """Summary information about a group returned in role assignment responses.""" + + id: str + """Identifier for the group.""" + + created_at: int + """Unix timestamp (in seconds) when the group was created.""" + + name: str + """Display name of the group.""" + + object: Literal["group"] + """Always `group`.""" + + scim_managed: bool + """Whether the group is managed through SCIM.""" + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a group to a role.""" + + group: Group + """Summary information about a group returned in role assignment responses.""" + + object: Literal["group.role"] + """Always `group.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" diff --git a/src/openai/types/admin/organization/projects/groups/role_delete_response.py b/src/openai/types/admin/organization/projects/groups/role_delete_response.py new file mode 100644 index 0000000000..704de05117 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ......_models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/src/openai/types/admin/organization/projects/groups/role_list_params.py b/src/openai/types/admin/organization/projects/groups/role_list_params.py new file mode 100644 index 0000000000..ffdbe210d2 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/role_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + project_id: Required[str] + + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + project roles. + """ + + limit: int + """A limit on the number of project role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned project roles.""" diff --git a/src/openai/types/admin/organization/projects/groups/role_list_response.py b/src/openai/types/admin/organization/projects/groups/role_list_response.py new file mode 100644 index 0000000000..72934bde13 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/role_list_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleListResponse"] + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/projects/project_api_key.py b/src/openai/types/admin/organization/projects/project_api_key.py new file mode 100644 index 0000000000..69cb243ba3 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_api_key.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel +from .project_user import ProjectUser +from .project_service_account import ProjectServiceAccount + +__all__ = ["ProjectAPIKey", "Owner"] + + +class Owner(BaseModel): + service_account: Optional[ProjectServiceAccount] = None + """Represents an individual service account in a project.""" + + type: Optional[Literal["user", "service_account"]] = None + """`user` or `service_account`""" + + user: Optional[ProjectUser] = None + """Represents an individual user in a project.""" + + +class ProjectAPIKey(BaseModel): + """Represents an individual API key in a project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the API key was created""" + + last_used_at: int + """The Unix timestamp (in seconds) of when the API key was last used.""" + + name: str + """The name of the API key""" + + object: Literal["organization.project.api_key"] + """The object type, which is always `organization.project.api_key`""" + + owner: Owner + + redacted_value: str + """The redacted value of the API key""" diff --git a/src/openai/types/admin/organization/projects/project_group.py b/src/openai/types/admin/organization/projects/project_group.py new file mode 100644 index 0000000000..e80d815795 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_group.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectGroup"] + + +class ProjectGroup(BaseModel): + """Details about a group's membership in a project.""" + + created_at: int + """Unix timestamp (in seconds) when the group was granted project access.""" + + group_id: str + """Identifier of the group that has access to the project.""" + + group_name: str + """Display name of the group.""" + + object: Literal["project.group"] + """Always `project.group`.""" + + project_id: str + """Identifier of the project.""" diff --git a/src/openai/types/admin/organization/projects/project_rate_limit.py b/src/openai/types/admin/organization/projects/project_rate_limit.py new file mode 100644 index 0000000000..46ff1ef036 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_rate_limit.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectRateLimit"] + + +class ProjectRateLimit(BaseModel): + """Represents a project rate limit config.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + max_requests_per_1_minute: int + """The maximum requests per minute.""" + + max_tokens_per_1_minute: int + """The maximum tokens per minute.""" + + model: str + """The model this rate limit applies to.""" + + object: Literal["project.rate_limit"] + """The object type, which is always `project.rate_limit`""" + + batch_1_day_max_input_tokens: Optional[int] = None + """The maximum batch input tokens per day. Only present for relevant models.""" + + max_audio_megabytes_per_1_minute: Optional[int] = None + """The maximum audio megabytes per minute. Only present for relevant models.""" + + max_images_per_1_minute: Optional[int] = None + """The maximum images per minute. Only present for relevant models.""" + + max_requests_per_1_day: Optional[int] = None + """The maximum requests per day. Only present for relevant models.""" diff --git a/src/openai/types/admin/organization/projects/project_service_account.py b/src/openai/types/admin/organization/projects/project_service_account.py new file mode 100644 index 0000000000..ca7bf0bdae --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_service_account.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectServiceAccount"] + + +class ProjectServiceAccount(BaseModel): + """Represents an individual service account in a project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the service account was created""" + + name: str + """The name of the service account""" + + object: Literal["organization.project.service_account"] + """The object type, which is always `organization.project.service_account`""" + + role: Literal["owner", "member"] + """`owner` or `member`""" diff --git a/src/openai/types/admin/organization/projects/project_user.py b/src/openai/types/admin/organization/projects/project_user.py new file mode 100644 index 0000000000..68c73d1c9b --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_user.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectUser"] + + +class ProjectUser(BaseModel): + """Represents an individual user in a project.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + added_at: int + """The Unix timestamp (in seconds) of when the project was added.""" + + email: str + """The email address of the user""" + + name: str + """The name of the user""" + + object: Literal["organization.project.user"] + """The object type, which is always `organization.project.user`""" + + role: Literal["owner", "member"] + """`owner` or `member`""" diff --git a/src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py b/src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py new file mode 100644 index 0000000000..198fe94f19 --- /dev/null +++ b/src/openai/types/admin/organization/projects/rate_limit_list_rate_limits_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["RateLimitListRateLimitsParams"] + + +class RateLimitListRateLimitsParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + before: str + """A cursor for use in pagination. + + `before` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, beginning with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page + of the list. + """ + + limit: int + """A limit on the number of objects to be returned. The default is 100.""" diff --git a/src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py b/src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py new file mode 100644 index 0000000000..5d5e515515 --- /dev/null +++ b/src/openai/types/admin/organization/projects/rate_limit_update_rate_limit_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RateLimitUpdateRateLimitParams"] + + +class RateLimitUpdateRateLimitParams(TypedDict, total=False): + project_id: Required[str] + + batch_1_day_max_input_tokens: int + """The maximum batch input tokens per day. Only relevant for certain models.""" + + max_audio_megabytes_per_1_minute: int + """The maximum audio megabytes per minute. Only relevant for certain models.""" + + max_images_per_1_minute: int + """The maximum images per minute. Only relevant for certain models.""" + + max_requests_per_1_day: int + """The maximum requests per day. Only relevant for certain models.""" + + max_requests_per_1_minute: int + """The maximum requests per minute.""" + + max_tokens_per_1_minute: int + """The maximum tokens per minute.""" diff --git a/src/openai/types/admin/organization/projects/role_create_params.py b/src/openai/types/admin/organization/projects/role_create_params.py new file mode 100644 index 0000000000..e05c8c8a63 --- /dev/null +++ b/src/openai/types/admin/organization/projects/role_create_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + permissions: Required[SequenceNotStr[str]] + """Permissions to grant to the role.""" + + role_name: Required[str] + """Unique name for the role.""" + + description: Optional[str] + """Optional description of the role.""" diff --git a/src/openai/types/admin/organization/projects/role_delete_response.py b/src/openai/types/admin/organization/projects/role_delete_response.py new file mode 100644 index 0000000000..87fa8e6200 --- /dev/null +++ b/src/openai/types/admin/organization/projects/role_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a role.""" + + id: str + """Identifier of the deleted role.""" + + deleted: bool + """Whether the role was deleted.""" + + object: Literal["role.deleted"] + """Always `role.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/role_list_params.py b/src/openai/types/admin/organization/projects/role_list_params.py new file mode 100644 index 0000000000..88e957f886 --- /dev/null +++ b/src/openai/types/admin/organization/projects/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + roles. + """ + + limit: int + """A limit on the number of roles to return. Defaults to 1000.""" + + order: Literal["asc", "desc"] + """Sort order for the returned roles.""" diff --git a/src/openai/types/admin/organization/projects/role_update_params.py b/src/openai/types/admin/organization/projects/role_update_params.py new file mode 100644 index 0000000000..4d440c87cd --- /dev/null +++ b/src/openai/types/admin/organization/projects/role_update_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["RoleUpdateParams"] + + +class RoleUpdateParams(TypedDict, total=False): + project_id: Required[str] + + description: Optional[str] + """New description for the role.""" + + permissions: Optional[SequenceNotStr[str]] + """Updated set of permissions for the role.""" + + role_name: Optional[str] + """New name for the role.""" diff --git a/src/openai/types/admin/organization/projects/service_account_create_params.py b/src/openai/types/admin/organization/projects/service_account_create_params.py new file mode 100644 index 0000000000..409dcba500 --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_account_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ServiceAccountCreateParams"] + + +class ServiceAccountCreateParams(TypedDict, total=False): + name: Required[str] + """The name of the service account being created.""" diff --git a/src/openai/types/admin/organization/projects/service_account_create_response.py b/src/openai/types/admin/organization/projects/service_account_create_response.py new file mode 100644 index 0000000000..ce9cfa5530 --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_account_create_response.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ServiceAccountCreateResponse", "APIKey"] + + +class APIKey(BaseModel): + id: str + + created_at: int + + name: str + + object: Literal["organization.project.service_account.api_key"] + """The object type, which is always `organization.project.service_account.api_key`""" + + value: str + + +class ServiceAccountCreateResponse(BaseModel): + id: str + + api_key: APIKey + + created_at: int + + name: str + + object: Literal["organization.project.service_account"] + + role: Literal["member"] + """Service accounts can only have one role of type `member`""" diff --git a/src/openai/types/admin/organization/projects/service_account_delete_response.py b/src/openai/types/admin/organization/projects/service_account_delete_response.py new file mode 100644 index 0000000000..e67e635aab --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_account_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ServiceAccountDeleteResponse"] + + +class ServiceAccountDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.project.service_account.deleted"] diff --git a/src/openai/types/admin/organization/projects/service_account_list_params.py b/src/openai/types/admin/organization/projects/service_account_list_params.py new file mode 100644 index 0000000000..7f808e285a --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_account_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ServiceAccountListParams"] + + +class ServiceAccountListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/src/openai/types/admin/organization/projects/user_create_params.py b/src/openai/types/admin/organization/projects/user_create_params.py new file mode 100644 index 0000000000..dee9d126db --- /dev/null +++ b/src/openai/types/admin/organization/projects/user_create_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["UserCreateParams"] + + +class UserCreateParams(TypedDict, total=False): + role: Required[Literal["owner", "member"]] + """`owner` or `member`""" + + user_id: Required[str] + """The ID of the user.""" diff --git a/src/openai/types/admin/organization/projects/user_delete_response.py b/src/openai/types/admin/organization/projects/user_delete_response.py new file mode 100644 index 0000000000..271d3a4126 --- /dev/null +++ b/src/openai/types/admin/organization/projects/user_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserDeleteResponse"] + + +class UserDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.project.user.deleted"] diff --git a/src/openai/types/admin/organization/projects/user_list_params.py b/src/openai/types/admin/organization/projects/user_list_params.py new file mode 100644 index 0000000000..d561e907b1 --- /dev/null +++ b/src/openai/types/admin/organization/projects/user_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["UserListParams"] + + +class UserListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/src/openai/types/admin/organization/projects/user_update_params.py b/src/openai/types/admin/organization/projects/user_update_params.py new file mode 100644 index 0000000000..08b3e1a4e9 --- /dev/null +++ b/src/openai/types/admin/organization/projects/user_update_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["UserUpdateParams"] + + +class UserUpdateParams(TypedDict, total=False): + project_id: Required[str] + + role: Required[Literal["owner", "member"]] + """`owner` or `member`""" diff --git a/src/openai/types/admin/organization/projects/users/__init__.py b/src/openai/types/admin/organization/projects/users/__init__.py new file mode 100644 index 0000000000..ed464fde83 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse diff --git a/src/openai/types/admin/organization/projects/users/role_create_params.py b/src/openai/types/admin/organization/projects/users/role_create_params.py new file mode 100644 index 0000000000..2aba01e8a4 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/role_create_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + project_id: Required[str] + + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/src/openai/types/admin/organization/projects/users/role_create_response.py b/src/openai/types/admin/organization/projects/users/role_create_response.py new file mode 100644 index 0000000000..533df36500 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/role_create_response.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...role import Role +from ......_models import BaseModel +from ...organization_user import OrganizationUser + +__all__ = ["RoleCreateResponse"] + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a user to a role.""" + + object: Literal["user.role"] + """Always `user.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" + + user: OrganizationUser + """Represents an individual `user` within an organization.""" diff --git a/src/openai/types/admin/organization/projects/users/role_delete_response.py b/src/openai/types/admin/organization/projects/users/role_delete_response.py new file mode 100644 index 0000000000..704de05117 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ......_models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/src/openai/types/admin/organization/projects/users/role_list_params.py b/src/openai/types/admin/organization/projects/users/role_list_params.py new file mode 100644 index 0000000000..ffdbe210d2 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/role_list_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + project_id: Required[str] + + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + project roles. + """ + + limit: int + """A limit on the number of project role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned project roles.""" diff --git a/src/openai/types/admin/organization/projects/users/role_list_response.py b/src/openai/types/admin/organization/projects/users/role_list_response.py new file mode 100644 index 0000000000..72934bde13 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/role_list_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleListResponse"] + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/role.py b/src/openai/types/admin/organization/role.py new file mode 100644 index 0000000000..4795144c68 --- /dev/null +++ b/src/openai/types/admin/organization/role.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["Role"] + + +class Role(BaseModel): + """Details about a role that can be assigned through the public Roles API.""" + + id: str + """Identifier for the role.""" + + description: Optional[str] = None + """Optional description of the role.""" + + name: str + """Unique name for the role.""" + + object: Literal["role"] + """Always `role`.""" + + permissions: List[str] + """Permissions granted by the role.""" + + predefined_role: bool + """Whether the role is predefined and managed by OpenAI.""" + + resource_type: str + """ + Resource type the role is bound to (for example `api.organization` or + `api.project`). + """ diff --git a/src/openai/types/admin/organization/role_create_params.py b/src/openai/types/admin/organization/role_create_params.py new file mode 100644 index 0000000000..60aaeb7383 --- /dev/null +++ b/src/openai/types/admin/organization/role_create_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + permissions: Required[SequenceNotStr[str]] + """Permissions to grant to the role.""" + + role_name: Required[str] + """Unique name for the role.""" + + description: Optional[str] + """Optional description of the role.""" diff --git a/src/openai/types/admin/organization/role_delete_response.py b/src/openai/types/admin/organization/role_delete_response.py new file mode 100644 index 0000000000..934140d363 --- /dev/null +++ b/src/openai/types/admin/organization/role_delete_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after deleting a role.""" + + id: str + """Identifier of the deleted role.""" + + deleted: bool + """Whether the role was deleted.""" + + object: Literal["role.deleted"] + """Always `role.deleted`.""" diff --git a/src/openai/types/admin/organization/role_list_params.py b/src/openai/types/admin/organization/role_list_params.py new file mode 100644 index 0000000000..88e957f886 --- /dev/null +++ b/src/openai/types/admin/organization/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + roles. + """ + + limit: int + """A limit on the number of roles to return. Defaults to 1000.""" + + order: Literal["asc", "desc"] + """Sort order for the returned roles.""" diff --git a/src/openai/types/admin/organization/role_update_params.py b/src/openai/types/admin/organization/role_update_params.py new file mode 100644 index 0000000000..955ca170a8 --- /dev/null +++ b/src/openai/types/admin/organization/role_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["RoleUpdateParams"] + + +class RoleUpdateParams(TypedDict, total=False): + description: Optional[str] + """New description for the role.""" + + permissions: Optional[SequenceNotStr[str]] + """Updated set of permissions for the role.""" + + role_name: Optional[str] + """New name for the role.""" diff --git a/src/openai/types/admin/organization/usage_audio_speeches_params.py b/src/openai/types/admin/organization/usage_audio_speeches_params.py new file mode 100644 index 0000000000..5d3eff286c --- /dev/null +++ b/src/openai/types/admin/organization/usage_audio_speeches_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageAudioSpeechesParams"] + + +class UsageAudioSpeechesParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py new file mode 100644 index 0000000000..f41ecad30d --- /dev/null +++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageAudioSpeechesResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageAudioSpeechesResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_params.py b/src/openai/types/admin/organization/usage_audio_transcriptions_params.py new file mode 100644 index 0000000000..ca5cd13c78 --- /dev/null +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageAudioTranscriptionsParams"] + + +class UsageAudioTranscriptionsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py new file mode 100644 index 0000000000..2d847dd9ae --- /dev/null +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageAudioTranscriptionsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageAudioTranscriptionsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py new file mode 100644 index 0000000000..dd4b59c2ab --- /dev/null +++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageCodeInterpreterSessionsParams"] + + +class UsageCodeInterpreterSessionsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py new file mode 100644 index 0000000000..e4634d9a6f --- /dev/null +++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageCodeInterpreterSessionsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageCodeInterpreterSessionsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_completions_params.py b/src/openai/types/admin/organization/usage_completions_params.py new file mode 100644 index 0000000000..e7dbe2187e --- /dev/null +++ b/src/openai/types/admin/organization/usage_completions_params.py @@ -0,0 +1,63 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageCompletionsParams"] + + +class UsageCompletionsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + batch: bool + """If `true`, return batch jobs only. + + If `false`, return non-batch jobs only. By default, return both. + """ + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "batch", "service_tier"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model`, `batch`, + `service_tier` or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py new file mode 100644 index 0000000000..a79c79c9bb --- /dev/null +++ b/src/openai/types/admin/organization/usage_completions_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageCompletionsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageCompletionsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_costs_params.py b/src/openai/types/admin/organization/usage_costs_params.py new file mode 100644 index 0000000000..e848643339 --- /dev/null +++ b/src/openai/types/admin/organization/usage_costs_params.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageCostsParams"] + + +class UsageCostsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only costs for these API keys.""" + + bucket_width: Literal["1d"] + """Width of each time bucket in response. + + Currently only `1d` is supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "line_item", "api_key_id"]] + """Group the costs by the specified fields. + + Support fields include `project_id`, `line_item`, `api_key_id` and any + combination of them. + """ + + limit: int + """A limit on the number of buckets to be returned. + + Limit can range between 1 and 180, and the default is 7. + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only costs for these projects.""" diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py new file mode 100644 index 0000000000..004e818242 --- /dev/null +++ b/src/openai/types/admin/organization/usage_costs_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageCostsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageCostsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_embeddings_params.py b/src/openai/types/admin/organization/usage_embeddings_params.py new file mode 100644 index 0000000000..56c9107b51 --- /dev/null +++ b/src/openai/types/admin/organization/usage_embeddings_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageEmbeddingsParams"] + + +class UsageEmbeddingsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py new file mode 100644 index 0000000000..f14208507c --- /dev/null +++ b/src/openai/types/admin/organization/usage_embeddings_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageEmbeddingsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageEmbeddingsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_images_params.py b/src/openai/types/admin/organization/usage_images_params.py new file mode 100644 index 0000000000..4cce8c456a --- /dev/null +++ b/src/openai/types/admin/organization/usage_images_params.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageImagesParams"] + + +class UsageImagesParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "size", "source"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model`, `size`, + `source` or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + sizes: List[Literal["256x256", "512x512", "1024x1024", "1792x1792", "1024x1792"]] + """Return only usages for these image sizes. + + Possible values are `256x256`, `512x512`, `1024x1024`, `1792x1792`, `1024x1792` + or any combination of them. + """ + + sources: List[Literal["image.generation", "image.edit", "image.variation"]] + """Return only usages for these sources. + + Possible values are `image.generation`, `image.edit`, `image.variation` or any + combination of them. + """ + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py new file mode 100644 index 0000000000..0188a51bc4 --- /dev/null +++ b/src/openai/types/admin/organization/usage_images_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageImagesResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageImagesResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_moderations_params.py b/src/openai/types/admin/organization/usage_moderations_params.py new file mode 100644 index 0000000000..acb401b9a8 --- /dev/null +++ b/src/openai/types/admin/organization/usage_moderations_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageModerationsParams"] + + +class UsageModerationsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model` or any + combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py new file mode 100644 index 0000000000..fc99593e5d --- /dev/null +++ b/src/openai/types/admin/organization/usage_moderations_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageModerationsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageModerationsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_vector_stores_params.py b/src/openai/types/admin/organization/usage_vector_stores_params.py new file mode 100644 index 0000000000..bfb8dcede4 --- /dev/null +++ b/src/openai/types/admin/organization/usage_vector_stores_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageVectorStoresParams"] + + +class UsageVectorStoresParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py new file mode 100644 index 0000000000..f17e867af0 --- /dev/null +++ b/src/openai/types/admin/organization/usage_vector_stores_response.py @@ -0,0 +1,390 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageVectorStoresResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + num_sessions: Optional[int] = None + """The number of code interpreter sessions.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + result: List[DataResult] + + start_time: int + + +class UsageVectorStoresResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: str + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/user_delete_response.py b/src/openai/types/admin/organization/user_delete_response.py new file mode 100644 index 0000000000..b8fc8c994a --- /dev/null +++ b/src/openai/types/admin/organization/user_delete_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["UserDeleteResponse"] + + +class UserDeleteResponse(BaseModel): + id: str + + deleted: bool + + object: Literal["organization.user.deleted"] diff --git a/src/openai/types/admin/organization/user_list_params.py b/src/openai/types/admin/organization/user_list_params.py new file mode 100644 index 0000000000..7bcfc78979 --- /dev/null +++ b/src/openai/types/admin/organization/user_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UserListParams"] + + +class UserListParams(TypedDict, total=False): + after: str + """A cursor for use in pagination. + + `after` is an object ID that defines your place in the list. For instance, if + you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include after=obj_foo in order to fetch the next page of the + list. + """ + + emails: SequenceNotStr[str] + """Filter by the email address of users.""" + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ diff --git a/src/openai/types/admin/organization/user_update_params.py b/src/openai/types/admin/organization/user_update_params.py new file mode 100644 index 0000000000..bc276120e3 --- /dev/null +++ b/src/openai/types/admin/organization/user_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["UserUpdateParams"] + + +class UserUpdateParams(TypedDict, total=False): + role: Required[Literal["owner", "reader"]] + """`owner` or `reader`""" diff --git a/src/openai/types/admin/organization/users/__init__.py b/src/openai/types/admin/organization/users/__init__.py new file mode 100644 index 0000000000..ed464fde83 --- /dev/null +++ b/src/openai/types/admin/organization/users/__init__.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .role_list_params import RoleListParams as RoleListParams +from .role_create_params import RoleCreateParams as RoleCreateParams +from .role_list_response import RoleListResponse as RoleListResponse +from .role_create_response import RoleCreateResponse as RoleCreateResponse +from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse diff --git a/src/openai/types/admin/organization/users/role_create_params.py b/src/openai/types/admin/organization/users/role_create_params.py new file mode 100644 index 0000000000..0ebc196eef --- /dev/null +++ b/src/openai/types/admin/organization/users/role_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["RoleCreateParams"] + + +class RoleCreateParams(TypedDict, total=False): + role_id: Required[str] + """Identifier of the role to assign.""" diff --git a/src/openai/types/admin/organization/users/role_create_response.py b/src/openai/types/admin/organization/users/role_create_response.py new file mode 100644 index 0000000000..0b989ad461 --- /dev/null +++ b/src/openai/types/admin/organization/users/role_create_response.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..role import Role +from ....._models import BaseModel +from ..organization_user import OrganizationUser + +__all__ = ["RoleCreateResponse"] + + +class RoleCreateResponse(BaseModel): + """Role assignment linking a user to a role.""" + + object: Literal["user.role"] + """Always `user.role`.""" + + role: Role + """Details about a role that can be assigned through the public Roles API.""" + + user: OrganizationUser + """Represents an individual `user` within an organization.""" diff --git a/src/openai/types/admin/organization/users/role_delete_response.py b/src/openai/types/admin/organization/users/role_delete_response.py new file mode 100644 index 0000000000..fb6a111614 --- /dev/null +++ b/src/openai/types/admin/organization/users/role_delete_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ....._models import BaseModel + +__all__ = ["RoleDeleteResponse"] + + +class RoleDeleteResponse(BaseModel): + """Confirmation payload returned after unassigning a role.""" + + deleted: bool + """Whether the assignment was removed.""" + + object: str + """ + Identifier for the deleted assignment, such as `group.role.deleted` or + `user.role.deleted`. + """ diff --git a/src/openai/types/admin/organization/users/role_list_params.py b/src/openai/types/admin/organization/users/role_list_params.py new file mode 100644 index 0000000000..451a1a2045 --- /dev/null +++ b/src/openai/types/admin/organization/users/role_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["RoleListParams"] + + +class RoleListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the value from the previous response's `next` field to continue listing + organization roles. + """ + + limit: int + """A limit on the number of organization role assignments to return.""" + + order: Literal["asc", "desc"] + """Sort order for the returned organization roles.""" diff --git a/src/openai/types/admin/organization/users/role_list_response.py b/src/openai/types/admin/organization/users/role_list_response.py new file mode 100644 index 0000000000..337d517ba1 --- /dev/null +++ b/src/openai/types/admin/organization/users/role_list_response.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleListResponse"] + + +class RoleListResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/tests/api_resources/admin/organization/groups/__init__.py b/tests/api_resources/admin/organization/groups/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/organization/groups/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/groups/test_roles.py b/tests/api_resources/admin/organization/groups/test_roles.py new file mode 100644 index 0000000000..73e8feabdb --- /dev/null +++ b/tests/api_resources/admin/organization/groups/test_roles.py @@ -0,0 +1,305 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization.groups import ( + RoleListResponse, + RoleCreateResponse, + RoleDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRoles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + role = client.admin.organization.groups.roles.create( + group_id="group_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.groups.roles.with_raw_response.create( + group_id="group_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.groups.roles.with_streaming_response.create( + group_id="group_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.create( + group_id="", + role_id="role_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + role = client.admin.organization.groups.roles.list( + group_id="group_id", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.groups.roles.list( + group_id="group_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.groups.roles.with_raw_response.list( + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.groups.roles.with_streaming_response.list( + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.list( + group_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + role = client.admin.organization.groups.roles.delete( + role_id="role_id", + group_id="group_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.groups.roles.with_raw_response.delete( + role_id="role_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.groups.roles.with_streaming_response.delete( + role_id="role_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.delete( + role_id="role_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.delete( + role_id="", + group_id="group_id", + ) + + +class TestAsyncRoles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.groups.roles.create( + group_id="group_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.roles.with_raw_response.create( + group_id="group_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.roles.with_streaming_response.create( + group_id="group_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.create( + group_id="", + role_id="role_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.groups.roles.list( + group_id="group_id", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.groups.roles.list( + group_id="group_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.roles.with_raw_response.list( + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.roles.with_streaming_response.list( + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.list( + group_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.groups.roles.delete( + role_id="role_id", + group_id="group_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.roles.with_raw_response.delete( + role_id="role_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.roles.with_streaming_response.delete( + role_id="role_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.delete( + role_id="role_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.delete( + role_id="", + group_id="group_id", + ) diff --git a/tests/api_resources/admin/organization/groups/test_users.py b/tests/api_resources/admin/organization/groups/test_users.py new file mode 100644 index 0000000000..cfa6354f99 --- /dev/null +++ b/tests/api_resources/admin/organization/groups/test_users.py @@ -0,0 +1,305 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization import OrganizationUser +from openai.types.admin.organization.groups import ( + UserCreateResponse, + UserDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestUsers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + user = client.admin.organization.groups.users.create( + group_id="group_id", + user_id="user_id", + ) + assert_matches_type(UserCreateResponse, user, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.groups.users.with_raw_response.create( + group_id="group_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserCreateResponse, user, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.groups.users.with_streaming_response.create( + group_id="group_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserCreateResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.create( + group_id="", + user_id="user_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + user = client.admin.organization.groups.users.list( + group_id="group_id", + ) + assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + user = client.admin.organization.groups.users.list( + group_id="group_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.groups.users.with_raw_response.list( + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.groups.users.with_streaming_response.list( + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.list( + group_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + user = client.admin.organization.groups.users.delete( + user_id="user_id", + group_id="group_id", + ) + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.groups.users.with_raw_response.delete( + user_id="user_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.groups.users.with_streaming_response.delete( + user_id="user_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.delete( + user_id="user_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.delete( + user_id="", + group_id="group_id", + ) + + +class TestAsyncUsers: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.groups.users.create( + group_id="group_id", + user_id="user_id", + ) + assert_matches_type(UserCreateResponse, user, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.users.with_raw_response.create( + group_id="group_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserCreateResponse, user, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.users.with_streaming_response.create( + group_id="group_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserCreateResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.create( + group_id="", + user_id="user_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.groups.users.list( + group_id="group_id", + ) + assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.groups.users.list( + group_id="group_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.users.with_raw_response.list( + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.users.with_streaming_response.list( + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.list( + group_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.groups.users.delete( + user_id="user_id", + group_id="group_id", + ) + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.users.with_raw_response.delete( + user_id="user_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.users.with_streaming_response.delete( + user_id="user_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.delete( + user_id="user_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.delete( + user_id="", + group_id="group_id", + ) diff --git a/tests/api_resources/admin/organization/projects/__init__.py b/tests/api_resources/admin/organization/projects/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/projects/groups/__init__.py b/tests/api_resources/admin/organization/projects/groups/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/groups/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/projects/groups/test_roles.py b/tests/api_resources/admin/organization/projects/groups/test_roles.py new file mode 100644 index 0000000000..4cfc957962 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/groups/test_roles.py @@ -0,0 +1,373 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization.projects.groups import ( + RoleListResponse, + RoleCreateResponse, + RoleDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRoles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + role = client.admin.organization.projects.groups.roles.create( + group_id="group_id", + project_id="project_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.roles.with_raw_response.create( + group_id="group_id", + project_id="project_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.roles.with_streaming_response.create( + group_id="group_id", + project_id="project_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.create( + group_id="group_id", + project_id="", + role_id="role_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.create( + group_id="", + project_id="project_id", + role_id="role_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + role = client.admin.organization.projects.groups.roles.list( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.projects.groups.roles.list( + group_id="group_id", + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.roles.with_raw_response.list( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.roles.with_streaming_response.list( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.list( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.list( + group_id="", + project_id="project_id", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + role = client.admin.organization.projects.groups.roles.delete( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.roles.with_streaming_response.delete( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="role_id", + project_id="", + group_id="group_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="", + project_id="project_id", + group_id="group_id", + ) + + +class TestAsyncRoles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.groups.roles.create( + group_id="group_id", + project_id="project_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.roles.with_raw_response.create( + group_id="group_id", + project_id="project_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.roles.with_streaming_response.create( + group_id="group_id", + project_id="project_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.create( + group_id="group_id", + project_id="", + role_id="role_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.create( + group_id="", + project_id="project_id", + role_id="role_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.groups.roles.list( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.groups.roles.list( + group_id="group_id", + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.roles.with_raw_response.list( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.roles.with_streaming_response.list( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.list( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.list( + group_id="", + project_id="project_id", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.groups.roles.delete( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.roles.with_streaming_response.delete( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="role_id", + project_id="", + group_id="group_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.delete( + role_id="", + project_id="project_id", + group_id="group_id", + ) diff --git a/tests/api_resources/admin/organization/projects/test_api_keys.py b/tests/api_resources/admin/organization/projects/test_api_keys.py new file mode 100644 index 0000000000..2f8ab56056 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_api_keys.py @@ -0,0 +1,311 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization.projects import ProjectAPIKey, APIKeyDeleteResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAPIKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + api_key = client.admin.organization.projects.api_keys.retrieve( + key_id="key_id", + project_id="project_id", + ) + assert_matches_type(ProjectAPIKey, api_key, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.api_keys.with_raw_response.retrieve( + key_id="key_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(ProjectAPIKey, api_key, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.api_keys.with_streaming_response.retrieve( + key_id="key_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(ProjectAPIKey, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.api_keys.with_raw_response.retrieve( + key_id="key_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + client.admin.organization.projects.api_keys.with_raw_response.retrieve( + key_id="", + project_id="project_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + api_key = client.admin.organization.projects.api_keys.list( + project_id="project_id", + ) + assert_matches_type(SyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + api_key = client.admin.organization.projects.api_keys.list( + project_id="project_id", + after="after", + limit=0, + ) + assert_matches_type(SyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.api_keys.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.api_keys.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.api_keys.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + api_key = client.admin.organization.projects.api_keys.delete( + key_id="key_id", + project_id="project_id", + ) + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.api_keys.with_raw_response.delete( + key_id="key_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.api_keys.with_streaming_response.delete( + key_id="key_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.api_keys.with_raw_response.delete( + key_id="key_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + client.admin.organization.projects.api_keys.with_raw_response.delete( + key_id="", + project_id="project_id", + ) + + +class TestAsyncAPIKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + api_key = await async_client.admin.organization.projects.api_keys.retrieve( + key_id="key_id", + project_id="project_id", + ) + assert_matches_type(ProjectAPIKey, api_key, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.api_keys.with_raw_response.retrieve( + key_id="key_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(ProjectAPIKey, api_key, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.api_keys.with_streaming_response.retrieve( + key_id="key_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(ProjectAPIKey, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.api_keys.with_raw_response.retrieve( + key_id="key_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + await async_client.admin.organization.projects.api_keys.with_raw_response.retrieve( + key_id="", + project_id="project_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + api_key = await async_client.admin.organization.projects.api_keys.list( + project_id="project_id", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + api_key = await async_client.admin.organization.projects.api_keys.list( + project_id="project_id", + after="after", + limit=0, + ) + assert_matches_type(AsyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.api_keys.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.api_keys.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.api_keys.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + api_key = await async_client.admin.organization.projects.api_keys.delete( + key_id="key_id", + project_id="project_id", + ) + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.api_keys.with_raw_response.delete( + key_id="key_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.api_keys.with_streaming_response.delete( + key_id="key_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.api_keys.with_raw_response.delete( + key_id="key_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + await async_client.admin.organization.projects.api_keys.with_raw_response.delete( + key_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/test_certificates.py b/tests/api_resources/admin/organization/projects/test_certificates.py new file mode 100644 index 0000000000..8ed72d6112 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_certificates.py @@ -0,0 +1,289 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import Certificate + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCertificates: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + certificate = client.admin.organization.projects.certificates.list( + project_id="project_id", + ) + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + certificate = client.admin.organization.projects.certificates.list( + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.certificates.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.certificates.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.certificates.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_activate(self, client: OpenAI) -> None: + certificate = client.admin.organization.projects.certificates.activate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_raw_response_activate(self, client: OpenAI) -> None: + response = client.admin.organization.projects.certificates.with_raw_response.activate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_streaming_response_activate(self, client: OpenAI) -> None: + with client.admin.organization.projects.certificates.with_streaming_response.activate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_activate(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.certificates.with_raw_response.activate( + project_id="", + certificate_ids=["cert_abc"], + ) + + @parametrize + def test_method_deactivate(self, client: OpenAI) -> None: + certificate = client.admin.organization.projects.certificates.deactivate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_raw_response_deactivate(self, client: OpenAI) -> None: + response = client.admin.organization.projects.certificates.with_raw_response.deactivate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_streaming_response_deactivate(self, client: OpenAI) -> None: + with client.admin.organization.projects.certificates.with_streaming_response.deactivate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_deactivate(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.certificates.with_raw_response.deactivate( + project_id="", + certificate_ids=["cert_abc"], + ) + + +class TestAsyncCertificates: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.projects.certificates.list( + project_id="project_id", + ) + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.projects.certificates.list( + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.certificates.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.certificates.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.certificates.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_activate(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.projects.certificates.activate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_raw_response_activate(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.certificates.with_raw_response.activate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_streaming_response_activate(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.certificates.with_streaming_response.activate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_activate(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.certificates.with_raw_response.activate( + project_id="", + certificate_ids=["cert_abc"], + ) + + @parametrize + async def test_method_deactivate(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.projects.certificates.deactivate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_raw_response_deactivate(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.certificates.with_raw_response.deactivate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_streaming_response_deactivate(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.certificates.with_streaming_response.deactivate( + project_id="project_id", + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_deactivate(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.certificates.with_raw_response.deactivate( + project_id="", + certificate_ids=["cert_abc"], + ) diff --git a/tests/api_resources/admin/organization/projects/test_groups.py b/tests/api_resources/admin/organization/projects/test_groups.py new file mode 100644 index 0000000000..57d768e738 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_groups.py @@ -0,0 +1,312 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization.projects import ( + ProjectGroup, + GroupDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGroups: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.create( + project_id="project_id", + group_id="group_id", + role="role", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.with_raw_response.create( + project_id="project_id", + group_id="group_id", + role="role", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.with_streaming_response.create( + project_id="project_id", + group_id="group_id", + role="role", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.create( + project_id="", + group_id="group_id", + role="role", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.list( + project_id="project_id", + ) + assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.list( + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.delete( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.with_raw_response.delete( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.with_streaming_response.delete( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.delete( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.delete( + group_id="", + project_id="project_id", + ) + + +class TestAsyncGroups: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.create( + project_id="project_id", + group_id="group_id", + role="role", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.with_raw_response.create( + project_id="project_id", + group_id="group_id", + role="role", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.with_streaming_response.create( + project_id="project_id", + group_id="group_id", + role="role", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.create( + project_id="", + group_id="group_id", + role="role", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.list( + project_id="project_id", + ) + assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.list( + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.delete( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.with_raw_response.delete( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.with_streaming_response.delete( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.delete( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.delete( + group_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/test_rate_limits.py b/tests/api_resources/admin/organization/projects/test_rate_limits.py new file mode 100644 index 0000000000..c06077614b --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_rate_limits.py @@ -0,0 +1,247 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization.projects import ( + ProjectRateLimit, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRateLimits: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list_rate_limits(self, client: OpenAI) -> None: + rate_limit = client.admin.organization.projects.rate_limits.list_rate_limits( + project_id="project_id", + ) + assert_matches_type(SyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + @parametrize + def test_method_list_rate_limits_with_all_params(self, client: OpenAI) -> None: + rate_limit = client.admin.organization.projects.rate_limits.list_rate_limits( + project_id="project_id", + after="after", + before="before", + limit=0, + ) + assert_matches_type(SyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + @parametrize + def test_raw_response_list_rate_limits(self, client: OpenAI) -> None: + response = client.admin.organization.projects.rate_limits.with_raw_response.list_rate_limits( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rate_limit = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + @parametrize + def test_streaming_response_list_rate_limits(self, client: OpenAI) -> None: + with client.admin.organization.projects.rate_limits.with_streaming_response.list_rate_limits( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rate_limit = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list_rate_limits(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.rate_limits.with_raw_response.list_rate_limits( + project_id="", + ) + + @parametrize + def test_method_update_rate_limit(self, client: OpenAI) -> None: + rate_limit = client.admin.organization.projects.rate_limits.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + ) + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + @parametrize + def test_method_update_rate_limit_with_all_params(self, client: OpenAI) -> None: + rate_limit = client.admin.organization.projects.rate_limits.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + batch_1_day_max_input_tokens=0, + max_audio_megabytes_per_1_minute=0, + max_images_per_1_minute=0, + max_requests_per_1_day=0, + max_requests_per_1_minute=0, + max_tokens_per_1_minute=0, + ) + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + @parametrize + def test_raw_response_update_rate_limit(self, client: OpenAI) -> None: + response = client.admin.organization.projects.rate_limits.with_raw_response.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rate_limit = response.parse() + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + @parametrize + def test_streaming_response_update_rate_limit(self, client: OpenAI) -> None: + with client.admin.organization.projects.rate_limits.with_streaming_response.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rate_limit = response.parse() + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update_rate_limit(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.rate_limits.with_raw_response.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `rate_limit_id` but received ''"): + client.admin.organization.projects.rate_limits.with_raw_response.update_rate_limit( + rate_limit_id="", + project_id="project_id", + ) + + +class TestAsyncRateLimits: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list_rate_limits(self, async_client: AsyncOpenAI) -> None: + rate_limit = await async_client.admin.organization.projects.rate_limits.list_rate_limits( + project_id="project_id", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + @parametrize + async def test_method_list_rate_limits_with_all_params(self, async_client: AsyncOpenAI) -> None: + rate_limit = await async_client.admin.organization.projects.rate_limits.list_rate_limits( + project_id="project_id", + after="after", + before="before", + limit=0, + ) + assert_matches_type(AsyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + @parametrize + async def test_raw_response_list_rate_limits(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.rate_limits.with_raw_response.list_rate_limits( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rate_limit = response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + @parametrize + async def test_streaming_response_list_rate_limits(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.rate_limits.with_streaming_response.list_rate_limits( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rate_limit = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectRateLimit], rate_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list_rate_limits(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.rate_limits.with_raw_response.list_rate_limits( + project_id="", + ) + + @parametrize + async def test_method_update_rate_limit(self, async_client: AsyncOpenAI) -> None: + rate_limit = await async_client.admin.organization.projects.rate_limits.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + ) + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + @parametrize + async def test_method_update_rate_limit_with_all_params(self, async_client: AsyncOpenAI) -> None: + rate_limit = await async_client.admin.organization.projects.rate_limits.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + batch_1_day_max_input_tokens=0, + max_audio_megabytes_per_1_minute=0, + max_images_per_1_minute=0, + max_requests_per_1_day=0, + max_requests_per_1_minute=0, + max_tokens_per_1_minute=0, + ) + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + @parametrize + async def test_raw_response_update_rate_limit(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.rate_limits.with_raw_response.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + rate_limit = response.parse() + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + @parametrize + async def test_streaming_response_update_rate_limit(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.rate_limits.with_streaming_response.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + rate_limit = await response.parse() + assert_matches_type(ProjectRateLimit, rate_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update_rate_limit(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.rate_limits.with_raw_response.update_rate_limit( + rate_limit_id="rate_limit_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `rate_limit_id` but received ''"): + await async_client.admin.organization.projects.rate_limits.with_raw_response.update_rate_limit( + rate_limit_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/test_roles.py b/tests/api_resources/admin/organization/projects/test_roles.py new file mode 100644 index 0000000000..697aa09f3a --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_roles.py @@ -0,0 +1,450 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization import Role +from openai.types.admin.organization.projects import ( + RoleDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRoles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + description="description", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.roles.with_raw_response.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.roles.with_streaming_response.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.create( + project_id="", + permissions=["string"], + role_name="role_name", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.update( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.update( + role_id="role_id", + project_id="project_id", + description="description", + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.roles.with_raw_response.update( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.roles.with_streaming_response.update( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.update( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.update( + role_id="", + project_id="project_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.list( + project_id="project_id", + ) + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.list( + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.roles.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.roles.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.delete( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.roles.with_streaming_response.delete( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.delete( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.delete( + role_id="", + project_id="project_id", + ) + + +class TestAsyncRoles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + description="description", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.roles.with_raw_response.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.roles.with_streaming_response.create( + project_id="project_id", + permissions=["string"], + role_name="role_name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.create( + project_id="", + permissions=["string"], + role_name="role_name", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.update( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.update( + role_id="role_id", + project_id="project_id", + description="description", + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.roles.with_raw_response.update( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.roles.with_streaming_response.update( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.update( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.update( + role_id="", + project_id="project_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.list( + project_id="project_id", + ) + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.list( + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.roles.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.roles.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.delete( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.roles.with_streaming_response.delete( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.delete( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.delete( + role_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/test_service_accounts.py b/tests/api_resources/admin/organization/projects/test_service_accounts.py new file mode 100644 index 0000000000..7c94283323 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_service_accounts.py @@ -0,0 +1,399 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization.projects import ( + ProjectServiceAccount, + ServiceAccountCreateResponse, + ServiceAccountDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestServiceAccounts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.create( + project_id="project_id", + name="name", + ) + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.service_accounts.with_raw_response.create( + project_id="project_id", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.service_accounts.with_streaming_response.create( + project_id="project_id", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = response.parse() + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.create( + project_id="", + name="name", + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.retrieve( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.service_accounts.with_raw_response.retrieve( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.service_accounts.with_streaming_response.retrieve( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.retrieve( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.retrieve( + service_account_id="", + project_id="project_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.list( + project_id="project_id", + ) + assert_matches_type(SyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.list( + project_id="project_id", + after="after", + limit=0, + ) + assert_matches_type(SyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.service_accounts.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.service_accounts.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.delete( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ServiceAccountDeleteResponse, service_account, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.service_accounts.with_raw_response.delete( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ServiceAccountDeleteResponse, service_account, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.service_accounts.with_streaming_response.delete( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = response.parse() + assert_matches_type(ServiceAccountDeleteResponse, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.delete( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.delete( + service_account_id="", + project_id="project_id", + ) + + +class TestAsyncServiceAccounts: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.create( + project_id="project_id", + name="name", + ) + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.service_accounts.with_raw_response.create( + project_id="project_id", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.service_accounts.with_streaming_response.create( + project_id="project_id", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = await response.parse() + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.create( + project_id="", + name="name", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.retrieve( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.service_accounts.with_raw_response.retrieve( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.service_accounts.with_streaming_response.retrieve( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = await response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.retrieve( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.retrieve( + service_account_id="", + project_id="project_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.list( + project_id="project_id", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.list( + project_id="project_id", + after="after", + limit=0, + ) + assert_matches_type(AsyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.service_accounts.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.service_accounts.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectServiceAccount], service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.delete( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ServiceAccountDeleteResponse, service_account, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.service_accounts.with_raw_response.delete( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ServiceAccountDeleteResponse, service_account, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.service_accounts.with_streaming_response.delete( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = await response.parse() + assert_matches_type(ServiceAccountDeleteResponse, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.delete( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.delete( + service_account_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/test_users.py b/tests/api_resources/admin/organization/projects/test_users.py new file mode 100644 index 0000000000..30ad94fe3b --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_users.py @@ -0,0 +1,512 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization.projects import ( + ProjectUser, + UserDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestUsers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.create( + project_id="project_id", + role="owner", + user_id="user_id", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.with_raw_response.create( + project_id="project_id", + role="owner", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.with_streaming_response.create( + project_id="project_id", + role="owner", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.create( + project_id="", + role="owner", + user_id="user_id", + ) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.retrieve( + user_id="user_id", + project_id="project_id", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.with_raw_response.retrieve( + user_id="user_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.with_streaming_response.retrieve( + user_id="user_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.retrieve( + user_id="user_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.retrieve( + user_id="", + project_id="project_id", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.update( + user_id="user_id", + project_id="project_id", + role="owner", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.with_raw_response.update( + user_id="user_id", + project_id="project_id", + role="owner", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.with_streaming_response.update( + user_id="user_id", + project_id="project_id", + role="owner", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.update( + user_id="user_id", + project_id="", + role="owner", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.update( + user_id="", + project_id="project_id", + role="owner", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.list( + project_id="project_id", + ) + assert_matches_type(SyncConversationCursorPage[ProjectUser], user, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.list( + project_id="project_id", + after="after", + limit=0, + ) + assert_matches_type(SyncConversationCursorPage[ProjectUser], user, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectUser], user, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectUser], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.delete( + user_id="user_id", + project_id="project_id", + ) + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.with_raw_response.delete( + user_id="user_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.with_streaming_response.delete( + user_id="user_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.delete( + user_id="user_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.with_raw_response.delete( + user_id="", + project_id="project_id", + ) + + +class TestAsyncUsers: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.create( + project_id="project_id", + role="owner", + user_id="user_id", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.with_raw_response.create( + project_id="project_id", + role="owner", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.with_streaming_response.create( + project_id="project_id", + role="owner", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.create( + project_id="", + role="owner", + user_id="user_id", + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.retrieve( + user_id="user_id", + project_id="project_id", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.with_raw_response.retrieve( + user_id="user_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.with_streaming_response.retrieve( + user_id="user_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.retrieve( + user_id="user_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.retrieve( + user_id="", + project_id="project_id", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.update( + user_id="user_id", + project_id="project_id", + role="owner", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.with_raw_response.update( + user_id="user_id", + project_id="project_id", + role="owner", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.with_streaming_response.update( + user_id="user_id", + project_id="project_id", + role="owner", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(ProjectUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.update( + user_id="user_id", + project_id="", + role="owner", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.update( + user_id="", + project_id="project_id", + role="owner", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.list( + project_id="project_id", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectUser], user, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.list( + project_id="project_id", + after="after", + limit=0, + ) + assert_matches_type(AsyncConversationCursorPage[ProjectUser], user, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectUser], user, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectUser], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.delete( + user_id="user_id", + project_id="project_id", + ) + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.with_raw_response.delete( + user_id="user_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.with_streaming_response.delete( + user_id="user_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.delete( + user_id="user_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.with_raw_response.delete( + user_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/users/__init__.py b/tests/api_resources/admin/organization/projects/users/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/users/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/projects/users/test_roles.py b/tests/api_resources/admin/organization/projects/users/test_roles.py new file mode 100644 index 0000000000..c39840646b --- /dev/null +++ b/tests/api_resources/admin/organization/projects/users/test_roles.py @@ -0,0 +1,373 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization.projects.users import ( + RoleListResponse, + RoleCreateResponse, + RoleDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRoles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + role = client.admin.organization.projects.users.roles.create( + user_id="user_id", + project_id="project_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.roles.with_raw_response.create( + user_id="user_id", + project_id="project_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.roles.with_streaming_response.create( + user_id="user_id", + project_id="project_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.create( + user_id="user_id", + project_id="", + role_id="role_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.create( + user_id="", + project_id="project_id", + role_id="role_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + role = client.admin.organization.projects.users.roles.list( + user_id="user_id", + project_id="project_id", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.projects.users.roles.list( + user_id="user_id", + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.roles.with_raw_response.list( + user_id="user_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.roles.with_streaming_response.list( + user_id="user_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.list( + user_id="user_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.list( + user_id="", + project_id="project_id", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + role = client.admin.organization.projects.users.roles.delete( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.roles.with_streaming_response.delete( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="role_id", + project_id="", + user_id="user_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="", + project_id="project_id", + user_id="user_id", + ) + + +class TestAsyncRoles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.users.roles.create( + user_id="user_id", + project_id="project_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.roles.with_raw_response.create( + user_id="user_id", + project_id="project_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.roles.with_streaming_response.create( + user_id="user_id", + project_id="project_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.create( + user_id="user_id", + project_id="", + role_id="role_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.create( + user_id="", + project_id="project_id", + role_id="role_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.users.roles.list( + user_id="user_id", + project_id="project_id", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.users.roles.list( + user_id="user_id", + project_id="project_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.roles.with_raw_response.list( + user_id="user_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.roles.with_streaming_response.list( + user_id="user_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.list( + user_id="user_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.list( + user_id="", + project_id="project_id", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.users.roles.delete( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.roles.with_streaming_response.delete( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="role_id", + project_id="", + user_id="user_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="role_id", + project_id="project_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.delete( + role_id="", + project_id="project_id", + user_id="user_id", + ) diff --git a/tests/api_resources/admin/organization/test_admin_api_keys.py b/tests/api_resources/admin/organization/test_admin_api_keys.py new file mode 100644 index 0000000000..3b6e7dec37 --- /dev/null +++ b/tests/api_resources/admin/organization/test_admin_api_keys.py @@ -0,0 +1,310 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization import ( + AdminAPIKey, + AdminAPIKeyDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAdminAPIKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + admin_api_key = client.admin.organization.admin_api_keys.create( + name="New Admin Key", + ) + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.admin_api_keys.with_raw_response.create( + name="New Admin Key", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.admin_api_keys.with_streaming_response.create( + name="New Admin Key", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + admin_api_key = client.admin.organization.admin_api_keys.retrieve( + "key_id", + ) + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.admin_api_keys.with_raw_response.retrieve( + "key_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.admin_api_keys.with_streaming_response.retrieve( + "key_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + client.admin.organization.admin_api_keys.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + admin_api_key = client.admin.organization.admin_api_keys.list() + assert_matches_type(SyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + admin_api_key = client.admin.organization.admin_api_keys.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.admin_api_keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(SyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.admin_api_keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = response.parse() + assert_matches_type(SyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + admin_api_key = client.admin.organization.admin_api_keys.delete( + "key_id", + ) + assert_matches_type(AdminAPIKeyDeleteResponse, admin_api_key, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.admin_api_keys.with_raw_response.delete( + "key_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(AdminAPIKeyDeleteResponse, admin_api_key, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.admin_api_keys.with_streaming_response.delete( + "key_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = response.parse() + assert_matches_type(AdminAPIKeyDeleteResponse, admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + client.admin.organization.admin_api_keys.with_raw_response.delete( + "", + ) + + +class TestAsyncAdminAPIKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + admin_api_key = await async_client.admin.organization.admin_api_keys.create( + name="New Admin Key", + ) + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.admin_api_keys.with_raw_response.create( + name="New Admin Key", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.admin_api_keys.with_streaming_response.create( + name="New Admin Key", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = await response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + admin_api_key = await async_client.admin.organization.admin_api_keys.retrieve( + "key_id", + ) + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.admin_api_keys.with_raw_response.retrieve( + "key_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.admin_api_keys.with_streaming_response.retrieve( + "key_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = await response.parse() + assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + await async_client.admin.organization.admin_api_keys.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + admin_api_key = await async_client.admin.organization.admin_api_keys.list() + assert_matches_type(AsyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + admin_api_key = await async_client.admin.organization.admin_api_keys.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.admin_api_keys.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(AsyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.admin_api_keys.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = await response.parse() + assert_matches_type(AsyncCursorPage[AdminAPIKey], admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + admin_api_key = await async_client.admin.organization.admin_api_keys.delete( + "key_id", + ) + assert_matches_type(AdminAPIKeyDeleteResponse, admin_api_key, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.admin_api_keys.with_raw_response.delete( + "key_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + admin_api_key = response.parse() + assert_matches_type(AdminAPIKeyDeleteResponse, admin_api_key, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.admin_api_keys.with_streaming_response.delete( + "key_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + admin_api_key = await response.parse() + assert_matches_type(AdminAPIKeyDeleteResponse, admin_api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + await async_client.admin.organization.admin_api_keys.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/test_certificates.py b/tests/api_resources/admin/organization/test_certificates.py new file mode 100644 index 0000000000..a1f0053f13 --- /dev/null +++ b/tests/api_resources/admin/organization/test_certificates.py @@ -0,0 +1,550 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import ( + Certificate, + CertificateDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCertificates: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.create( + content="content", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.create( + content="content", + name="name", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.certificates.with_raw_response.create( + content="content", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.certificates.with_streaming_response.create( + content="content", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.retrieve( + certificate_id="certificate_id", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.retrieve( + certificate_id="certificate_id", + include=["content"], + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.certificates.with_raw_response.retrieve( + certificate_id="certificate_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.certificates.with_streaming_response.retrieve( + certificate_id="certificate_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): + client.admin.organization.certificates.with_raw_response.retrieve( + certificate_id="", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.update( + certificate_id="certificate_id", + name="name", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.certificates.with_raw_response.update( + certificate_id="certificate_id", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.certificates.with_streaming_response.update( + certificate_id="certificate_id", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): + client.admin.organization.certificates.with_raw_response.update( + certificate_id="", + name="name", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.list() + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.certificates.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.certificates.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.delete( + "certificate_id", + ) + assert_matches_type(CertificateDeleteResponse, certificate, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.certificates.with_raw_response.delete( + "certificate_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(CertificateDeleteResponse, certificate, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.certificates.with_streaming_response.delete( + "certificate_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(CertificateDeleteResponse, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): + client.admin.organization.certificates.with_raw_response.delete( + "", + ) + + @parametrize + def test_method_activate(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.activate( + certificate_ids=["cert_abc"], + ) + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_raw_response_activate(self, client: OpenAI) -> None: + response = client.admin.organization.certificates.with_raw_response.activate( + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_streaming_response_activate(self, client: OpenAI) -> None: + with client.admin.organization.certificates.with_streaming_response.activate( + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_deactivate(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.deactivate( + certificate_ids=["cert_abc"], + ) + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_raw_response_deactivate(self, client: OpenAI) -> None: + response = client.admin.organization.certificates.with_raw_response.deactivate( + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + @parametrize + def test_streaming_response_deactivate(self, client: OpenAI) -> None: + with client.admin.organization.certificates.with_streaming_response.deactivate( + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = response.parse() + assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncCertificates: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.create( + content="content", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.create( + content="content", + name="name", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.certificates.with_raw_response.create( + content="content", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.certificates.with_streaming_response.create( + content="content", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.retrieve( + certificate_id="certificate_id", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.retrieve( + certificate_id="certificate_id", + include=["content"], + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.certificates.with_raw_response.retrieve( + certificate_id="certificate_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.certificates.with_streaming_response.retrieve( + certificate_id="certificate_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): + await async_client.admin.organization.certificates.with_raw_response.retrieve( + certificate_id="", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.update( + certificate_id="certificate_id", + name="name", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.certificates.with_raw_response.update( + certificate_id="certificate_id", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.certificates.with_streaming_response.update( + certificate_id="certificate_id", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(Certificate, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): + await async_client.admin.organization.certificates.with_raw_response.update( + certificate_id="", + name="name", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.list() + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.certificates.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.certificates.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.delete( + "certificate_id", + ) + assert_matches_type(CertificateDeleteResponse, certificate, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.certificates.with_raw_response.delete( + "certificate_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(CertificateDeleteResponse, certificate, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.certificates.with_streaming_response.delete( + "certificate_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(CertificateDeleteResponse, certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): + await async_client.admin.organization.certificates.with_raw_response.delete( + "", + ) + + @parametrize + async def test_method_activate(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.activate( + certificate_ids=["cert_abc"], + ) + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_raw_response_activate(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.certificates.with_raw_response.activate( + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_streaming_response_activate(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.certificates.with_streaming_response.activate( + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_deactivate(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.deactivate( + certificate_ids=["cert_abc"], + ) + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_raw_response_deactivate(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.certificates.with_raw_response.deactivate( + certificate_ids=["cert_abc"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + certificate = response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + @parametrize + async def test_streaming_response_deactivate(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.certificates.with_streaming_response.deactivate( + certificate_ids=["cert_abc"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + certificate = await response.parse() + assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_groups.py b/tests/api_resources/admin/organization/test_groups.py new file mode 100644 index 0000000000..b37469625b --- /dev/null +++ b/tests/api_resources/admin/organization/test_groups.py @@ -0,0 +1,319 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization import ( + Group, + GroupDeleteResponse, + GroupUpdateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGroups: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + group = client.admin.organization.groups.create( + name="x", + ) + assert_matches_type(Group, group, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.groups.with_raw_response.create( + name="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.groups.with_streaming_response.create( + name="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + group = client.admin.organization.groups.update( + group_id="group_id", + name="x", + ) + assert_matches_type(GroupUpdateResponse, group, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.groups.with_raw_response.update( + group_id="group_id", + name="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(GroupUpdateResponse, group, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.groups.with_streaming_response.update( + group_id="group_id", + name="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(GroupUpdateResponse, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.with_raw_response.update( + group_id="", + name="x", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + group = client.admin.organization.groups.list() + assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + group = client.admin.organization.groups.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.groups.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.groups.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + group = client.admin.organization.groups.delete( + "group_id", + ) + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.groups.with_raw_response.delete( + "group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.groups.with_streaming_response.delete( + "group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.with_raw_response.delete( + "", + ) + + +class TestAsyncGroups: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.groups.create( + name="x", + ) + assert_matches_type(Group, group, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.with_raw_response.create( + name="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.with_streaming_response.create( + name="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(Group, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.groups.update( + group_id="group_id", + name="x", + ) + assert_matches_type(GroupUpdateResponse, group, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.with_raw_response.update( + group_id="group_id", + name="x", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(GroupUpdateResponse, group, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.with_streaming_response.update( + group_id="group_id", + name="x", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(GroupUpdateResponse, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.with_raw_response.update( + group_id="", + name="x", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.groups.list() + assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.groups.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.groups.delete( + "group_id", + ) + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.with_raw_response.delete( + "group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.with_streaming_response.delete( + "group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(GroupDeleteResponse, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/test_invites.py b/tests/api_resources/admin/organization/test_invites.py new file mode 100644 index 0000000000..ddad6e6e31 --- /dev/null +++ b/tests/api_resources/admin/organization/test_invites.py @@ -0,0 +1,339 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import Invite, InviteDeleteResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInvites: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + invite = client.admin.organization.invites.create( + email="email", + role="reader", + ) + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + invite = client.admin.organization.invites.create( + email="email", + role="reader", + projects=[ + { + "id": "id", + "role": "member", + } + ], + ) + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.invites.with_raw_response.create( + email="email", + role="reader", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.invites.with_streaming_response.create( + email="email", + role="reader", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + invite = client.admin.organization.invites.retrieve( + "invite_id", + ) + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.invites.with_raw_response.retrieve( + "invite_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.invites.with_streaming_response.retrieve( + "invite_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `invite_id` but received ''"): + client.admin.organization.invites.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + invite = client.admin.organization.invites.list() + assert_matches_type(SyncConversationCursorPage[Invite], invite, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + invite = client.admin.organization.invites.list( + after="after", + limit=0, + ) + assert_matches_type(SyncConversationCursorPage[Invite], invite, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.invites.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(SyncConversationCursorPage[Invite], invite, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.invites.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = response.parse() + assert_matches_type(SyncConversationCursorPage[Invite], invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + invite = client.admin.organization.invites.delete( + "invite_id", + ) + assert_matches_type(InviteDeleteResponse, invite, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.invites.with_raw_response.delete( + "invite_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(InviteDeleteResponse, invite, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.invites.with_streaming_response.delete( + "invite_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = response.parse() + assert_matches_type(InviteDeleteResponse, invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `invite_id` but received ''"): + client.admin.organization.invites.with_raw_response.delete( + "", + ) + + +class TestAsyncInvites: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + invite = await async_client.admin.organization.invites.create( + email="email", + role="reader", + ) + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + invite = await async_client.admin.organization.invites.create( + email="email", + role="reader", + projects=[ + { + "id": "id", + "role": "member", + } + ], + ) + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.invites.with_raw_response.create( + email="email", + role="reader", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.invites.with_streaming_response.create( + email="email", + role="reader", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = await response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + invite = await async_client.admin.organization.invites.retrieve( + "invite_id", + ) + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.invites.with_raw_response.retrieve( + "invite_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.invites.with_streaming_response.retrieve( + "invite_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = await response.parse() + assert_matches_type(Invite, invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `invite_id` but received ''"): + await async_client.admin.organization.invites.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + invite = await async_client.admin.organization.invites.list() + assert_matches_type(AsyncConversationCursorPage[Invite], invite, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + invite = await async_client.admin.organization.invites.list( + after="after", + limit=0, + ) + assert_matches_type(AsyncConversationCursorPage[Invite], invite, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.invites.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(AsyncConversationCursorPage[Invite], invite, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.invites.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = await response.parse() + assert_matches_type(AsyncConversationCursorPage[Invite], invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + invite = await async_client.admin.organization.invites.delete( + "invite_id", + ) + assert_matches_type(InviteDeleteResponse, invite, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.invites.with_raw_response.delete( + "invite_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + invite = response.parse() + assert_matches_type(InviteDeleteResponse, invite, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.invites.with_streaming_response.delete( + "invite_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + invite = await response.parse() + assert_matches_type(InviteDeleteResponse, invite, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `invite_id` but received ''"): + await async_client.admin.organization.invites.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/test_projects.py b/tests/api_resources/admin/organization/test_projects.py new file mode 100644 index 0000000000..49c18827de --- /dev/null +++ b/tests/api_resources/admin/organization/test_projects.py @@ -0,0 +1,407 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import Project + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestProjects: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + project = client.admin.organization.projects.create( + name="name", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + project = client.admin.organization.projects.create( + name="name", + geography="US", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.with_raw_response.create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.with_streaming_response.create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + project = client.admin.organization.projects.retrieve( + "project_id", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + project = client.admin.organization.projects.update( + project_id="project_id", + name="name", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.with_raw_response.update( + project_id="project_id", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.with_streaming_response.update( + project_id="project_id", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.with_raw_response.update( + project_id="", + name="name", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + project = client.admin.organization.projects.list() + assert_matches_type(SyncConversationCursorPage[Project], project, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + project = client.admin.organization.projects.list( + after="after", + include_archived=True, + limit=0, + ) + assert_matches_type(SyncConversationCursorPage[Project], project, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(SyncConversationCursorPage[Project], project, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(SyncConversationCursorPage[Project], project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_archive(self, client: OpenAI) -> None: + project = client.admin.organization.projects.archive( + "project_id", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_raw_response_archive(self, client: OpenAI) -> None: + response = client.admin.organization.projects.with_raw_response.archive( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_streaming_response_archive(self, client: OpenAI) -> None: + with client.admin.organization.projects.with_streaming_response.archive( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_archive(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.with_raw_response.archive( + "", + ) + + +class TestAsyncProjects: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.create( + name="name", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.create( + name="name", + geography="US", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.with_raw_response.create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.with_streaming_response.create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.retrieve( + "project_id", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.update( + project_id="project_id", + name="name", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.with_raw_response.update( + project_id="project_id", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.with_streaming_response.update( + project_id="project_id", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.with_raw_response.update( + project_id="", + name="name", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.list() + assert_matches_type(AsyncConversationCursorPage[Project], project, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.list( + after="after", + include_archived=True, + limit=0, + ) + assert_matches_type(AsyncConversationCursorPage[Project], project, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(AsyncConversationCursorPage[Project], project, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(AsyncConversationCursorPage[Project], project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_archive(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.archive( + "project_id", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_raw_response_archive(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.with_raw_response.archive( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_streaming_response_archive(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.with_streaming_response.archive( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_archive(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.with_raw_response.archive( + "", + ) diff --git a/tests/api_resources/admin/organization/test_roles.py b/tests/api_resources/admin/organization/test_roles.py new file mode 100644 index 0000000000..1b4b44278b --- /dev/null +++ b/tests/api_resources/admin/organization/test_roles.py @@ -0,0 +1,354 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization import ( + Role, + RoleDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRoles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + role = client.admin.organization.roles.create( + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.roles.create( + permissions=["string"], + role_name="role_name", + description="description", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.roles.with_raw_response.create( + permissions=["string"], + role_name="role_name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.roles.with_streaming_response.create( + permissions=["string"], + role_name="role_name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + role = client.admin.organization.roles.update( + role_id="role_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.roles.update( + role_id="role_id", + description="description", + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.roles.with_raw_response.update( + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.roles.with_streaming_response.update( + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.roles.with_raw_response.update( + role_id="", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + role = client.admin.organization.roles.list() + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.roles.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.roles.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.roles.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + role = client.admin.organization.roles.delete( + "role_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.roles.with_raw_response.delete( + "role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.roles.with_streaming_response.delete( + "role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.roles.with_raw_response.delete( + "", + ) + + +class TestAsyncRoles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.create( + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.create( + permissions=["string"], + role_name="role_name", + description="description", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.roles.with_raw_response.create( + permissions=["string"], + role_name="role_name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.roles.with_streaming_response.create( + permissions=["string"], + role_name="role_name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.update( + role_id="role_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.update( + role_id="role_id", + description="description", + permissions=["string"], + role_name="role_name", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.roles.with_raw_response.update( + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.roles.with_streaming_response.update( + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.roles.with_raw_response.update( + role_id="", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.list() + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.list( + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.roles.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.roles.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.delete( + "role_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.roles.with_raw_response.delete( + "role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.roles.with_streaming_response.delete( + "role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.roles.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/test_usage.py b/tests/api_resources/admin/organization/test_usage.py new file mode 100644 index 0000000000..f9c4a8e2c5 --- /dev/null +++ b/tests/api_resources/admin/organization/test_usage.py @@ -0,0 +1,870 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization import ( + UsageCostsResponse, + UsageImagesResponse, + UsageEmbeddingsResponse, + UsageCompletionsResponse, + UsageModerationsResponse, + UsageVectorStoresResponse, + UsageAudioSpeechesResponse, + UsageAudioTranscriptionsResponse, + UsageCodeInterpreterSessionsResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestUsage: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_audio_speeches(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.audio_speeches( + start_time=0, + ) + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + @parametrize + def test_method_audio_speeches_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.audio_speeches( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_audio_speeches(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.audio_speeches( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_audio_speeches(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.audio_speeches( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_audio_transcriptions(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.audio_transcriptions( + start_time=0, + ) + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + @parametrize + def test_method_audio_transcriptions_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.audio_transcriptions( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_audio_transcriptions(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.audio_transcriptions( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_audio_transcriptions(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.audio_transcriptions( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_code_interpreter_sessions(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.code_interpreter_sessions( + start_time=0, + ) + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + @parametrize + def test_method_code_interpreter_sessions_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.code_interpreter_sessions( + start_time=0, + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + ) + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_code_interpreter_sessions(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.code_interpreter_sessions( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_code_interpreter_sessions(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.code_interpreter_sessions( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_completions(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.completions( + start_time=0, + ) + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + @parametrize + def test_method_completions_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.completions( + start_time=0, + api_key_ids=["string"], + batch=True, + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_completions(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.completions( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_completions(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.completions( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_costs(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.costs( + start_time=0, + ) + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + @parametrize + def test_method_costs_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.costs( + start_time=0, + api_key_ids=["string"], + bucket_width="1d", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + ) + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_costs(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.costs( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_costs(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.costs( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_embeddings(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.embeddings( + start_time=0, + ) + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + @parametrize + def test_method_embeddings_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.embeddings( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_embeddings(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.embeddings( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_embeddings(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.embeddings( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_images(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.images( + start_time=0, + ) + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + @parametrize + def test_method_images_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.images( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + sizes=["256x256"], + sources=["image.generation"], + user_ids=["string"], + ) + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_images(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.images( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_images(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.images( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_moderations(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.moderations( + start_time=0, + ) + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + @parametrize + def test_method_moderations_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.moderations( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_moderations(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.moderations( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_moderations(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.moderations( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_vector_stores(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.vector_stores( + start_time=0, + ) + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + @parametrize + def test_method_vector_stores_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.vector_stores( + start_time=0, + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + ) + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_vector_stores(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.vector_stores( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_vector_stores(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.vector_stores( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncUsage: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_audio_speeches(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.audio_speeches( + start_time=0, + ) + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + @parametrize + async def test_method_audio_speeches_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.audio_speeches( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_audio_speeches(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.audio_speeches( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_audio_speeches(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.audio_speeches( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageAudioSpeechesResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_audio_transcriptions(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.audio_transcriptions( + start_time=0, + ) + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + @parametrize + async def test_method_audio_transcriptions_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.audio_transcriptions( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_audio_transcriptions(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.audio_transcriptions( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_audio_transcriptions(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.audio_transcriptions( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageAudioTranscriptionsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_code_interpreter_sessions(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.code_interpreter_sessions( + start_time=0, + ) + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + @parametrize + async def test_method_code_interpreter_sessions_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.code_interpreter_sessions( + start_time=0, + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + ) + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_code_interpreter_sessions(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.code_interpreter_sessions( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_code_interpreter_sessions(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.code_interpreter_sessions( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageCodeInterpreterSessionsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_completions(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.completions( + start_time=0, + ) + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + @parametrize + async def test_method_completions_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.completions( + start_time=0, + api_key_ids=["string"], + batch=True, + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_completions(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.completions( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_completions(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.completions( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageCompletionsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_costs(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.costs( + start_time=0, + ) + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + @parametrize + async def test_method_costs_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.costs( + start_time=0, + api_key_ids=["string"], + bucket_width="1d", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + ) + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_costs(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.costs( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_costs(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.costs( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageCostsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_embeddings(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.embeddings( + start_time=0, + ) + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + @parametrize + async def test_method_embeddings_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.embeddings( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_embeddings(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.embeddings( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_embeddings(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.embeddings( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageEmbeddingsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_images(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.images( + start_time=0, + ) + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + @parametrize + async def test_method_images_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.images( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + sizes=["256x256"], + sources=["image.generation"], + user_ids=["string"], + ) + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_images(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.images( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_images(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.images( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageImagesResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_moderations(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.moderations( + start_time=0, + ) + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + @parametrize + async def test_method_moderations_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.moderations( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_moderations(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.moderations( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_moderations(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.moderations( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageModerationsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_vector_stores(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.vector_stores( + start_time=0, + ) + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + @parametrize + async def test_method_vector_stores_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.vector_stores( + start_time=0, + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + ) + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_vector_stores(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.vector_stores( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_vector_stores(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.vector_stores( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_users.py b/tests/api_resources/admin/organization/test_users.py new file mode 100644 index 0000000000..7350b6ecf5 --- /dev/null +++ b/tests/api_resources/admin/organization/test_users.py @@ -0,0 +1,329 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import OrganizationUser, UserDeleteResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestUsers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + user = client.admin.organization.users.retrieve( + "user_id", + ) + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.users.with_raw_response.retrieve( + "user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.users.with_streaming_response.retrieve( + "user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + user = client.admin.organization.users.update( + user_id="user_id", + role="owner", + ) + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.users.with_raw_response.update( + user_id="user_id", + role="owner", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.users.with_streaming_response.update( + user_id="user_id", + role="owner", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.with_raw_response.update( + user_id="", + role="owner", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + user = client.admin.organization.users.list() + assert_matches_type(SyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + user = client.admin.organization.users.list( + after="after", + emails=["string"], + limit=0, + ) + assert_matches_type(SyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.users.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(SyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.users.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(SyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + user = client.admin.organization.users.delete( + "user_id", + ) + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.users.with_raw_response.delete( + "user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.users.with_streaming_response.delete( + "user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.with_raw_response.delete( + "", + ) + + +class TestAsyncUsers: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.users.retrieve( + "user_id", + ) + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.with_raw_response.retrieve( + "user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.with_streaming_response.retrieve( + "user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.users.update( + user_id="user_id", + role="owner", + ) + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.with_raw_response.update( + user_id="user_id", + role="owner", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.with_streaming_response.update( + user_id="user_id", + role="owner", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(OrganizationUser, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.with_raw_response.update( + user_id="", + role="owner", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.users.list() + assert_matches_type(AsyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.users.list( + after="after", + emails=["string"], + limit=0, + ) + assert_matches_type(AsyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(AsyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(AsyncConversationCursorPage[OrganizationUser], user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.users.delete( + "user_id", + ) + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.with_raw_response.delete( + "user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.with_streaming_response.delete( + "user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserDeleteResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/users/__init__.py b/tests/api_resources/admin/organization/users/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/organization/users/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/users/test_roles.py b/tests/api_resources/admin/organization/users/test_roles.py new file mode 100644 index 0000000000..272c0459db --- /dev/null +++ b/tests/api_resources/admin/organization/users/test_roles.py @@ -0,0 +1,305 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.admin.organization.users import ( + RoleListResponse, + RoleCreateResponse, + RoleDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRoles: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + role = client.admin.organization.users.roles.create( + user_id="user_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.users.roles.with_raw_response.create( + user_id="user_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.users.roles.with_streaming_response.create( + user_id="user_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.create( + user_id="", + role_id="role_id", + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + role = client.admin.organization.users.roles.list( + user_id="user_id", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + role = client.admin.organization.users.roles.list( + user_id="user_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.users.roles.with_raw_response.list( + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.users.roles.with_streaming_response.list( + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.list( + user_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + role = client.admin.organization.users.roles.delete( + role_id="role_id", + user_id="user_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.users.roles.with_raw_response.delete( + role_id="role_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.users.roles.with_streaming_response.delete( + role_id="role_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.delete( + role_id="role_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.delete( + role_id="", + user_id="user_id", + ) + + +class TestAsyncRoles: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.users.roles.create( + user_id="user_id", + role_id="role_id", + ) + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.roles.with_raw_response.create( + user_id="user_id", + role_id="role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.roles.with_streaming_response.create( + user_id="user_id", + role_id="role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleCreateResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.create( + user_id="", + role_id="role_id", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.users.roles.list( + user_id="user_id", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.users.roles.list( + user_id="user_id", + after="after", + limit=0, + order="asc", + ) + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.roles.with_raw_response.list( + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.roles.with_streaming_response.list( + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.list( + user_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.users.roles.delete( + role_id="role_id", + user_id="user_id", + ) + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.roles.with_raw_response.delete( + role_id="role_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.roles.with_streaming_response.delete( + role_id="role_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleDeleteResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.delete( + role_id="role_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.delete( + role_id="", + user_id="user_id", + ) From b1417cc7702af9b9edf0519d03bd3d780ecf5047 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 14:48:54 +0000 Subject: [PATCH 330/408] feat(api): admin API updates --- .stats.yml | 6 +- api.md | 66 +++++++++++----- src/openai/pagination.py | 60 ++++++++++++++ .../admin/organization/admin_api_keys.py | 9 ++- .../admin/organization/certificates.py | 55 +++++++------ .../admin/organization/groups/groups.py | 10 +-- .../admin/organization/groups/roles.py | 10 +-- .../admin/organization/groups/users.py | 16 ++-- .../admin/organization/projects/api_keys.py | 40 ++++++---- .../organization/projects/certificates.py | 40 +++++----- .../organization/projects/groups/groups.py | 10 +-- .../organization/projects/groups/roles.py | 10 +-- .../admin/organization/projects/roles.py | 10 +-- .../organization/projects/users/roles.py | 10 +-- .../resources/admin/organization/roles.py | 10 +-- .../admin/organization/users/roles.py | 10 +-- .../admin/organization/users/users.py | 4 +- .../types/admin/organization/__init__.py | 4 + .../types/admin/organization/admin_api_key.py | 3 +- .../admin_api_key_create_response.py | 12 +++ .../admin_api_key_delete_response.py | 8 +- .../organization/audit_log_list_response.py | 6 +- .../types/admin/organization/certificate.py | 2 +- .../certificate_activate_response.py | 37 +++++++++ .../organization/certificate_create_params.py | 2 +- .../certificate_deactivate_response.py | 37 +++++++++ .../organization/certificate_list_response.py | 37 +++++++++ .../organization/certificate_update_params.py | 4 +- .../admin/organization/groups/__init__.py | 1 + .../groups/organization_group_user.py | 20 +++++ src/openai/types/admin/organization/invite.py | 8 +- .../admin/organization/projects/__init__.py | 3 + .../projects/certificate_activate_response.py | 37 +++++++++ .../certificate_deactivate_response.py | 37 +++++++++ .../projects/certificate_list_response.py | 37 +++++++++ .../organization/projects/project_api_key.py | 49 ++++++++++-- .../service_account_create_response.py | 3 +- .../usage_audio_speeches_response.py | 10 +-- .../usage_audio_transcriptions_response.py | 10 +-- ...sage_code_interpreter_sessions_response.py | 10 +-- .../usage_completions_response.py | 10 +-- .../organization/usage_costs_response.py | 10 +-- .../organization/usage_embeddings_response.py | 10 +-- .../organization/usage_images_response.py | 10 +-- .../usage_moderations_response.py | 10 +-- .../usage_vector_stores_response.py | 10 +-- .../admin/organization/user_update_params.py | 4 +- .../admin/organization/groups/test_roles.py | 18 ++--- .../admin/organization/groups/test_users.py | 20 ++--- .../projects/groups/test_roles.py | 18 ++--- .../organization/projects/test_api_keys.py | 48 +++++------ .../projects/test_certificates.py | 46 ++++++----- .../organization/projects/test_groups.py | 18 ++--- .../admin/organization/projects/test_roles.py | 18 ++--- .../organization/projects/users/test_roles.py | 18 ++--- .../admin/organization/test_admin_api_keys.py | 13 +-- .../admin/organization/test_certificates.py | 79 +++++++++++-------- .../admin/organization/test_groups.py | 18 ++--- .../admin/organization/test_roles.py | 18 ++--- .../admin/organization/test_users.py | 20 +++-- .../admin/organization/users/test_roles.py | 18 ++--- 61 files changed, 803 insertions(+), 384 deletions(-) create mode 100644 src/openai/types/admin/organization/admin_api_key_create_response.py create mode 100644 src/openai/types/admin/organization/certificate_activate_response.py create mode 100644 src/openai/types/admin/organization/certificate_deactivate_response.py create mode 100644 src/openai/types/admin/organization/certificate_list_response.py create mode 100644 src/openai/types/admin/organization/groups/organization_group_user.py create mode 100644 src/openai/types/admin/organization/projects/certificate_activate_response.py create mode 100644 src/openai/types/admin/organization/projects/certificate_deactivate_response.py create mode 100644 src/openai/types/admin/organization/projects/certificate_list_response.py diff --git a/.stats.yml b/.stats.yml index 7b55dfd466..c12fe18621 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5b00c10325d1747a1b7da6289fe47e18f3d69503925bd3b6c1c67bbadc5a7f8f.yml -openapi_spec_hash: 142dc3e8e2e0a4f1017102e1dd49a85b -config_hash: 53256195d26013f6fe5ee634fb1c92f6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-ea3c8bfb9cc34e798c5a473bb68ab5012344b1c99ab95377d0af4d908eb32a5d.yml +openapi_spec_hash: dbab3fdd7781b449ba3e9e1b5180c6ed +config_hash: 5d8a716125a61761563abbfc0d34e57c diff --git a/api.md b/api.md index f80a5b12c8..e04636937c 100644 --- a/api.md +++ b/api.md @@ -728,12 +728,16 @@ Methods: Types: ```python -from openai.types.admin.organization import AdminAPIKey, AdminAPIKeyDeleteResponse +from openai.types.admin.organization import ( + AdminAPIKey, + AdminAPIKeyCreateResponse, + AdminAPIKeyDeleteResponse, +) ``` Methods: -- client.admin.organization.admin_api_keys.create(\*\*params) -> AdminAPIKey +- client.admin.organization.admin_api_keys.create(\*\*params) -> AdminAPIKeyCreateResponse - client.admin.organization.admin_api_keys.retrieve(key_id) -> AdminAPIKey - client.admin.organization.admin_api_keys.list(\*\*params) -> SyncCursorPage[AdminAPIKey] - client.admin.organization.admin_api_keys.delete(key_id) -> AdminAPIKeyDeleteResponse @@ -813,7 +817,7 @@ from openai.types.admin.organization.users import ( Methods: - client.admin.organization.users.roles.create(user_id, \*\*params) -> RoleCreateResponse -- client.admin.organization.users.roles.list(user_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.users.roles.list(user_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.users.roles.delete(role_id, \*, user_id) -> RoleDeleteResponse ### Groups @@ -828,7 +832,7 @@ Methods: - client.admin.organization.groups.create(\*\*params) -> Group - client.admin.organization.groups.update(group_id, \*\*params) -> GroupUpdateResponse -- client.admin.organization.groups.list(\*\*params) -> SyncCursorPage[Group] +- client.admin.organization.groups.list(\*\*params) -> SyncNextCursorPage[Group] - client.admin.organization.groups.delete(group_id) -> GroupDeleteResponse #### Users @@ -836,13 +840,17 @@ Methods: Types: ```python -from openai.types.admin.organization.groups import UserCreateResponse, UserDeleteResponse +from openai.types.admin.organization.groups import ( + OrganizationGroupUser, + UserCreateResponse, + UserDeleteResponse, +) ``` Methods: - client.admin.organization.groups.users.create(group_id, \*\*params) -> UserCreateResponse -- client.admin.organization.groups.users.list(group_id, \*\*params) -> SyncCursorPage[OrganizationUser] +- client.admin.organization.groups.users.list(group_id, \*\*params) -> SyncNextCursorPage[OrganizationGroupUser] - client.admin.organization.groups.users.delete(user_id, \*, group_id) -> UserDeleteResponse #### Roles @@ -860,7 +868,7 @@ from openai.types.admin.organization.groups import ( Methods: - client.admin.organization.groups.roles.create(group_id, \*\*params) -> RoleCreateResponse -- client.admin.organization.groups.roles.list(group_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.groups.roles.list(group_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.groups.roles.delete(role_id, \*, group_id) -> RoleDeleteResponse ### Roles @@ -875,7 +883,7 @@ Methods: - client.admin.organization.roles.create(\*\*params) -> Role - client.admin.organization.roles.update(role_id, \*\*params) -> Role -- client.admin.organization.roles.list(\*\*params) -> SyncCursorPage[Role] +- client.admin.organization.roles.list(\*\*params) -> SyncNextCursorPage[Role] - client.admin.organization.roles.delete(role_id) -> RoleDeleteResponse ### Certificates @@ -883,7 +891,13 @@ Methods: Types: ```python -from openai.types.admin.organization import Certificate, CertificateDeleteResponse +from openai.types.admin.organization import ( + Certificate, + CertificateListResponse, + CertificateDeleteResponse, + CertificateActivateResponse, + CertificateDeactivateResponse, +) ``` Methods: @@ -891,10 +905,10 @@ Methods: - client.admin.organization.certificates.create(\*\*params) -> Certificate - client.admin.organization.certificates.retrieve(certificate_id, \*\*params) -> Certificate - client.admin.organization.certificates.update(certificate_id, \*\*params) -> Certificate -- client.admin.organization.certificates.list(\*\*params) -> SyncConversationCursorPage[Certificate] +- client.admin.organization.certificates.list(\*\*params) -> SyncConversationCursorPage[CertificateListResponse] - client.admin.organization.certificates.delete(certificate_id) -> CertificateDeleteResponse -- client.admin.organization.certificates.activate(\*\*params) -> SyncPage[Certificate] -- client.admin.organization.certificates.deactivate(\*\*params) -> SyncPage[Certificate] +- client.admin.organization.certificates.activate(\*\*params) -> SyncPage[CertificateActivateResponse] +- client.admin.organization.certificates.deactivate(\*\*params) -> SyncPage[CertificateDeactivateResponse] ### Projects @@ -943,7 +957,7 @@ from openai.types.admin.organization.projects.users import ( Methods: - client.admin.organization.projects.users.roles.create(user_id, \*, project_id, \*\*params) -> RoleCreateResponse -- client.admin.organization.projects.users.roles.list(user_id, \*, project_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.projects.users.roles.list(user_id, \*, project_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.projects.users.roles.delete(role_id, \*, project_id, user_id) -> RoleDeleteResponse #### ServiceAccounts @@ -975,9 +989,9 @@ from openai.types.admin.organization.projects import ProjectAPIKey, APIKeyDelete Methods: -- client.admin.organization.projects.api_keys.retrieve(key_id, \*, project_id) -> ProjectAPIKey +- client.admin.organization.projects.api_keys.retrieve(api_key_id, \*, project_id) -> ProjectAPIKey - client.admin.organization.projects.api_keys.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectAPIKey] -- client.admin.organization.projects.api_keys.delete(key_id, \*, project_id) -> APIKeyDeleteResponse +- client.admin.organization.projects.api_keys.delete(api_key_id, \*, project_id) -> APIKeyDeleteResponse #### RateLimits @@ -1003,7 +1017,7 @@ from openai.types.admin.organization.projects import ProjectGroup, GroupDeleteRe Methods: - client.admin.organization.projects.groups.create(project_id, \*\*params) -> ProjectGroup -- client.admin.organization.projects.groups.list(project_id, \*\*params) -> SyncCursorPage[ProjectGroup] +- client.admin.organization.projects.groups.list(project_id, \*\*params) -> SyncNextCursorPage[ProjectGroup] - client.admin.organization.projects.groups.delete(group_id, \*, project_id) -> GroupDeleteResponse ##### Roles @@ -1021,7 +1035,7 @@ from openai.types.admin.organization.projects.groups import ( Methods: - client.admin.organization.projects.groups.roles.create(group_id, \*, project_id, \*\*params) -> RoleCreateResponse -- client.admin.organization.projects.groups.roles.list(group_id, \*, project_id, \*\*params) -> SyncCursorPage[RoleListResponse] +- client.admin.organization.projects.groups.roles.list(group_id, \*, project_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.projects.groups.roles.delete(role_id, \*, project_id, group_id) -> RoleDeleteResponse #### Roles @@ -1036,16 +1050,26 @@ Methods: - client.admin.organization.projects.roles.create(project_id, \*\*params) -> Role - client.admin.organization.projects.roles.update(role_id, \*, project_id, \*\*params) -> Role -- client.admin.organization.projects.roles.list(project_id, \*\*params) -> SyncCursorPage[Role] +- client.admin.organization.projects.roles.list(project_id, \*\*params) -> SyncNextCursorPage[Role] - client.admin.organization.projects.roles.delete(role_id, \*, project_id) -> RoleDeleteResponse #### Certificates +Types: + +```python +from openai.types.admin.organization.projects import ( + CertificateListResponse, + CertificateActivateResponse, + CertificateDeactivateResponse, +) +``` + Methods: -- client.admin.organization.projects.certificates.list(project_id, \*\*params) -> SyncConversationCursorPage[Certificate] -- client.admin.organization.projects.certificates.activate(project_id, \*\*params) -> SyncPage[Certificate] -- client.admin.organization.projects.certificates.deactivate(project_id, \*\*params) -> SyncPage[Certificate] +- client.admin.organization.projects.certificates.list(project_id, \*\*params) -> SyncConversationCursorPage[CertificateListResponse] +- client.admin.organization.projects.certificates.activate(project_id, \*\*params) -> SyncPage[CertificateActivateResponse] +- client.admin.organization.projects.certificates.deactivate(project_id, \*\*params) -> SyncPage[CertificateDeactivateResponse] # [Responses](src/openai/resources/responses/api.md) diff --git a/src/openai/pagination.py b/src/openai/pagination.py index 4dd3788aa3..6cf0c8e7f5 100644 --- a/src/openai/pagination.py +++ b/src/openai/pagination.py @@ -12,6 +12,8 @@ "AsyncCursorPage", "SyncConversationCursorPage", "AsyncConversationCursorPage", + "SyncNextCursorPage", + "AsyncNextCursorPage", ] _T = TypeVar("_T") @@ -188,3 +190,61 @@ def next_page_info(self) -> Optional[PageInfo]: return None return PageInfo(params={"after": last_id}) + + +class SyncNextCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + next: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = self.next + if not next: + return None + + return PageInfo(params={"after": next}) + + +class AsyncNextCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + has_more: Optional[bool] = None + next: Optional[str] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next = self.next + if not next: + return None + + return PageInfo(params={"after": next}) diff --git a/src/openai/resources/admin/organization/admin_api_keys.py b/src/openai/resources/admin/organization/admin_api_keys.py index bf3ff7abd0..80ddc39b49 100644 --- a/src/openai/resources/admin/organization/admin_api_keys.py +++ b/src/openai/resources/admin/organization/admin_api_keys.py @@ -17,6 +17,7 @@ from ...._base_client import AsyncPaginator, make_request_options from ....types.admin.organization import admin_api_key_list_params, admin_api_key_create_params from ....types.admin.organization.admin_api_key import AdminAPIKey +from ....types.admin.organization.admin_api_key_create_response import AdminAPIKeyCreateResponse from ....types.admin.organization.admin_api_key_delete_response import AdminAPIKeyDeleteResponse __all__ = ["AdminAPIKeys", "AsyncAdminAPIKeys"] @@ -52,7 +53,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AdminAPIKey: + ) -> AdminAPIKeyCreateResponse: """ Create an organization admin API key @@ -75,7 +76,7 @@ def create( timeout=timeout, security={"admin_api_key_auth": True}, ), - cast_to=AdminAPIKey, + cast_to=AdminAPIKeyCreateResponse, ) def retrieve( @@ -239,7 +240,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AdminAPIKey: + ) -> AdminAPIKeyCreateResponse: """ Create an organization admin API key @@ -262,7 +263,7 @@ async def create( timeout=timeout, security={"admin_api_key_auth": True}, ), - cast_to=AdminAPIKey, + cast_to=AdminAPIKeyCreateResponse, ) async def retrieve( diff --git a/src/openai/resources/admin/organization/certificates.py b/src/openai/resources/admin/organization/certificates.py index 1cab1e3610..aa7826794d 100644 --- a/src/openai/resources/admin/organization/certificates.py +++ b/src/openai/resources/admin/organization/certificates.py @@ -24,7 +24,10 @@ certificate_deactivate_params, ) from ....types.admin.organization.certificate import Certificate +from ....types.admin.organization.certificate_list_response import CertificateListResponse from ....types.admin.organization.certificate_delete_response import CertificateDeleteResponse +from ....types.admin.organization.certificate_activate_response import CertificateActivateResponse +from ....types.admin.organization.certificate_deactivate_response import CertificateDeactivateResponse __all__ = ["Certificates", "AsyncCertificates"] @@ -52,7 +55,7 @@ def with_streaming_response(self) -> CertificatesWithStreamingResponse: def create( self, *, - content: str, + certificate: str, name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -69,7 +72,7 @@ def create( Organizations can upload up to 50 certificates. Args: - content: The certificate content in PEM format + certificate: The certificate content in PEM format name: An optional name for the certificate @@ -85,7 +88,7 @@ def create( "/organization/certificates", body=maybe_transform( { - "content": content, + "certificate": certificate, "name": name, }, certificate_create_params.CertificateCreateParams, @@ -148,7 +151,7 @@ def update( self, certificate_id: str, *, - name: str, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -198,7 +201,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncConversationCursorPage[Certificate]: + ) -> SyncConversationCursorPage[CertificateListResponse]: """ List uploaded certificates for this organization. @@ -224,7 +227,7 @@ def list( """ return self._get_api_list( "/organization/certificates", - page=SyncConversationCursorPage[Certificate], + page=SyncConversationCursorPage[CertificateListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -240,7 +243,7 @@ def list( ), security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateListResponse, ) def delete( @@ -292,7 +295,7 @@ def activate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[Certificate]: + ) -> SyncPage[CertificateActivateResponse]: """ Activate certificates at the organization level. @@ -309,7 +312,7 @@ def activate( """ return self._get_api_list( "/organization/certificates/activate", - page=SyncPage[Certificate], + page=SyncPage[CertificateActivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams ), @@ -320,7 +323,7 @@ def activate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateActivateResponse, method="post", ) @@ -334,7 +337,7 @@ def deactivate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[Certificate]: + ) -> SyncPage[CertificateDeactivateResponse]: """ Deactivate certificates at the organization level. @@ -351,7 +354,7 @@ def deactivate( """ return self._get_api_list( "/organization/certificates/deactivate", - page=SyncPage[Certificate], + page=SyncPage[CertificateDeactivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams ), @@ -362,7 +365,7 @@ def deactivate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateDeactivateResponse, method="post", ) @@ -390,7 +393,7 @@ def with_streaming_response(self) -> AsyncCertificatesWithStreamingResponse: async def create( self, *, - content: str, + certificate: str, name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -407,7 +410,7 @@ async def create( Organizations can upload up to 50 certificates. Args: - content: The certificate content in PEM format + certificate: The certificate content in PEM format name: An optional name for the certificate @@ -423,7 +426,7 @@ async def create( "/organization/certificates", body=await async_maybe_transform( { - "content": content, + "certificate": certificate, "name": name, }, certificate_create_params.CertificateCreateParams, @@ -488,7 +491,7 @@ async def update( self, certificate_id: str, *, - name: str, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -538,7 +541,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Certificate, AsyncConversationCursorPage[Certificate]]: + ) -> AsyncPaginator[CertificateListResponse, AsyncConversationCursorPage[CertificateListResponse]]: """ List uploaded certificates for this organization. @@ -564,7 +567,7 @@ def list( """ return self._get_api_list( "/organization/certificates", - page=AsyncConversationCursorPage[Certificate], + page=AsyncConversationCursorPage[CertificateListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -580,7 +583,7 @@ def list( ), security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateListResponse, ) async def delete( @@ -632,7 +635,7 @@ def activate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + ) -> AsyncPaginator[CertificateActivateResponse, AsyncPage[CertificateActivateResponse]]: """ Activate certificates at the organization level. @@ -649,7 +652,7 @@ def activate( """ return self._get_api_list( "/organization/certificates/activate", - page=AsyncPage[Certificate], + page=AsyncPage[CertificateActivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams ), @@ -660,7 +663,7 @@ def activate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateActivateResponse, method="post", ) @@ -674,7 +677,7 @@ def deactivate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + ) -> AsyncPaginator[CertificateDeactivateResponse, AsyncPage[CertificateDeactivateResponse]]: """ Deactivate certificates at the organization level. @@ -691,7 +694,7 @@ def deactivate( """ return self._get_api_list( "/organization/certificates/deactivate", - page=AsyncPage[Certificate], + page=AsyncPage[CertificateDeactivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams ), @@ -702,7 +705,7 @@ def deactivate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateDeactivateResponse, method="post", ) diff --git a/src/openai/resources/admin/organization/groups/groups.py b/src/openai/resources/admin/organization/groups/groups.py index 1daf07b1ab..80baa38926 100644 --- a/src/openai/resources/admin/organization/groups/groups.py +++ b/src/openai/resources/admin/organization/groups/groups.py @@ -28,7 +28,7 @@ from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncCursorPage, AsyncCursorPage +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage from ....._base_client import AsyncPaginator, make_request_options from .....types.admin.organization import group_list_params, group_create_params, group_update_params from .....types.admin.organization.group import Group @@ -157,7 +157,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[Group]: + ) -> SyncNextCursorPage[Group]: """ Lists all groups in the organization. @@ -182,7 +182,7 @@ def list( """ return self._get_api_list( "/organization/groups", - page=SyncCursorPage[Group], + page=SyncNextCursorPage[Group], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -358,7 +358,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Group, AsyncCursorPage[Group]]: + ) -> AsyncPaginator[Group, AsyncNextCursorPage[Group]]: """ Lists all groups in the organization. @@ -383,7 +383,7 @@ def list( """ return self._get_api_list( "/organization/groups", - page=AsyncCursorPage[Group], + page=AsyncNextCursorPage[Group], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/groups/roles.py b/src/openai/resources/admin/organization/groups/roles.py index ca62ee9b35..c85efccfef 100644 --- a/src/openai/resources/admin/organization/groups/roles.py +++ b/src/openai/resources/admin/organization/groups/roles.py @@ -12,7 +12,7 @@ from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncCursorPage, AsyncCursorPage +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage from ....._base_client import AsyncPaginator, make_request_options from .....types.admin.organization.groups import role_list_params, role_create_params from .....types.admin.organization.groups.role_list_response import RoleListResponse @@ -96,7 +96,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[RoleListResponse]: + ) -> SyncNextCursorPage[RoleListResponse]: """ Lists the organization roles assigned to a group within the organization. @@ -120,7 +120,7 @@ def list( raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return self._get_api_list( path_template("/organization/groups/{group_id}/roles", group_id=group_id), - page=SyncCursorPage[RoleListResponse], + page=SyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -254,7 +254,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: """ Lists the organization roles assigned to a group within the organization. @@ -278,7 +278,7 @@ def list( raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return self._get_api_list( path_template("/organization/groups/{group_id}/roles", group_id=group_id), - page=AsyncCursorPage[RoleListResponse], + page=AsyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/groups/users.py b/src/openai/resources/admin/organization/groups/users.py index 648d6221cf..89a2996e13 100644 --- a/src/openai/resources/admin/organization/groups/users.py +++ b/src/openai/resources/admin/organization/groups/users.py @@ -12,12 +12,12 @@ from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncCursorPage, AsyncCursorPage +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage from ....._base_client import AsyncPaginator, make_request_options from .....types.admin.organization.groups import user_list_params, user_create_params -from .....types.admin.organization.organization_user import OrganizationUser from .....types.admin.organization.groups.user_create_response import UserCreateResponse from .....types.admin.organization.groups.user_delete_response import UserDeleteResponse +from .....types.admin.organization.groups.organization_group_user import OrganizationGroupUser __all__ = ["Users", "AsyncUsers"] @@ -96,7 +96,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[OrganizationUser]: + ) -> SyncNextCursorPage[OrganizationGroupUser]: """ Lists the users assigned to a group. @@ -121,7 +121,7 @@ def list( raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return self._get_api_list( path_template("/organization/groups/{group_id}/users", group_id=group_id), - page=SyncCursorPage[OrganizationUser], + page=SyncNextCursorPage[OrganizationGroupUser], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -137,7 +137,7 @@ def list( ), security={"admin_api_key_auth": True}, ), - model=OrganizationUser, + model=OrganizationGroupUser, ) def delete( @@ -255,7 +255,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[OrganizationUser, AsyncCursorPage[OrganizationUser]]: + ) -> AsyncPaginator[OrganizationGroupUser, AsyncNextCursorPage[OrganizationGroupUser]]: """ Lists the users assigned to a group. @@ -280,7 +280,7 @@ def list( raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return self._get_api_list( path_template("/organization/groups/{group_id}/users", group_id=group_id), - page=AsyncCursorPage[OrganizationUser], + page=AsyncNextCursorPage[OrganizationGroupUser], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -296,7 +296,7 @@ def list( ), security={"admin_api_key_auth": True}, ), - model=OrganizationUser, + model=OrganizationGroupUser, ) async def delete( diff --git a/src/openai/resources/admin/organization/projects/api_keys.py b/src/openai/resources/admin/organization/projects/api_keys.py index 8902ce56ff..1517d213e0 100644 --- a/src/openai/resources/admin/organization/projects/api_keys.py +++ b/src/openai/resources/admin/organization/projects/api_keys.py @@ -41,7 +41,7 @@ def with_streaming_response(self) -> APIKeysWithStreamingResponse: def retrieve( self, - key_id: str, + api_key_id: str, *, project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -65,11 +65,13 @@ def retrieve( """ if not project_id: raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") - if not key_id: - raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") return self._get( path_template( - "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, ), options=make_request_options( extra_headers=extra_headers, @@ -138,7 +140,7 @@ def list( def delete( self, - key_id: str, + api_key_id: str, *, project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -165,11 +167,13 @@ def delete( """ if not project_id: raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") - if not key_id: - raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") return self._delete( path_template( - "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, ), options=make_request_options( extra_headers=extra_headers, @@ -204,7 +208,7 @@ def with_streaming_response(self) -> AsyncAPIKeysWithStreamingResponse: async def retrieve( self, - key_id: str, + api_key_id: str, *, project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -228,11 +232,13 @@ async def retrieve( """ if not project_id: raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") - if not key_id: - raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") return await self._get( path_template( - "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, ), options=make_request_options( extra_headers=extra_headers, @@ -301,7 +307,7 @@ def list( async def delete( self, - key_id: str, + api_key_id: str, *, project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -328,11 +334,13 @@ async def delete( """ if not project_id: raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") - if not key_id: - raise ValueError(f"Expected a non-empty value for `key_id` but received {key_id!r}") + if not api_key_id: + raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}") return await self._delete( path_template( - "/organization/projects/{project_id}/api_keys/{key_id}", project_id=project_id, key_id=key_id + "/organization/projects/{project_id}/api_keys/{api_key_id}", + project_id=project_id, + api_key_id=api_key_id, ), options=make_request_options( extra_headers=extra_headers, diff --git a/src/openai/resources/admin/organization/projects/certificates.py b/src/openai/resources/admin/organization/projects/certificates.py index 3ff2111293..ec449d570a 100644 --- a/src/openai/resources/admin/organization/projects/certificates.py +++ b/src/openai/resources/admin/organization/projects/certificates.py @@ -19,7 +19,9 @@ certificate_activate_params, certificate_deactivate_params, ) -from .....types.admin.organization.certificate import Certificate +from .....types.admin.organization.projects.certificate_list_response import CertificateListResponse +from .....types.admin.organization.projects.certificate_activate_response import CertificateActivateResponse +from .....types.admin.organization.projects.certificate_deactivate_response import CertificateDeactivateResponse __all__ = ["Certificates", "AsyncCertificates"] @@ -57,7 +59,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncConversationCursorPage[Certificate]: + ) -> SyncConversationCursorPage[CertificateListResponse]: """ List certificates for this project. @@ -85,7 +87,7 @@ def list( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/certificates", project_id=project_id), - page=SyncConversationCursorPage[Certificate], + page=SyncConversationCursorPage[CertificateListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -101,7 +103,7 @@ def list( ), security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateListResponse, ) def activate( @@ -115,7 +117,7 @@ def activate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[Certificate]: + ) -> SyncPage[CertificateActivateResponse]: """ Activate certificates at the project level. @@ -134,7 +136,7 @@ def activate( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/certificates/activate", project_id=project_id), - page=SyncPage[Certificate], + page=SyncPage[CertificateActivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams ), @@ -145,7 +147,7 @@ def activate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateActivateResponse, method="post", ) @@ -160,7 +162,7 @@ def deactivate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[Certificate]: + ) -> SyncPage[CertificateDeactivateResponse]: """Deactivate certificates at the project level. You can atomically and @@ -179,7 +181,7 @@ def deactivate( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/certificates/deactivate", project_id=project_id), - page=SyncPage[Certificate], + page=SyncPage[CertificateDeactivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams ), @@ -190,7 +192,7 @@ def deactivate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateDeactivateResponse, method="post", ) @@ -228,7 +230,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Certificate, AsyncConversationCursorPage[Certificate]]: + ) -> AsyncPaginator[CertificateListResponse, AsyncConversationCursorPage[CertificateListResponse]]: """ List certificates for this project. @@ -256,7 +258,7 @@ def list( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/certificates", project_id=project_id), - page=AsyncConversationCursorPage[Certificate], + page=AsyncConversationCursorPage[CertificateListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -272,7 +274,7 @@ def list( ), security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateListResponse, ) def activate( @@ -286,7 +288,7 @@ def activate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + ) -> AsyncPaginator[CertificateActivateResponse, AsyncPage[CertificateActivateResponse]]: """ Activate certificates at the project level. @@ -305,7 +307,7 @@ def activate( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/certificates/activate", project_id=project_id), - page=AsyncPage[Certificate], + page=AsyncPage[CertificateActivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_activate_params.CertificateActivateParams ), @@ -316,7 +318,7 @@ def activate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateActivateResponse, method="post", ) @@ -331,7 +333,7 @@ def deactivate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Certificate, AsyncPage[Certificate]]: + ) -> AsyncPaginator[CertificateDeactivateResponse, AsyncPage[CertificateDeactivateResponse]]: """Deactivate certificates at the project level. You can atomically and @@ -350,7 +352,7 @@ def deactivate( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/certificates/deactivate", project_id=project_id), - page=AsyncPage[Certificate], + page=AsyncPage[CertificateDeactivateResponse], body=maybe_transform( {"certificate_ids": certificate_ids}, certificate_deactivate_params.CertificateDeactivateParams ), @@ -361,7 +363,7 @@ def deactivate( timeout=timeout, security={"admin_api_key_auth": True}, ), - model=Certificate, + model=CertificateDeactivateResponse, method="post", ) diff --git a/src/openai/resources/admin/organization/projects/groups/groups.py b/src/openai/resources/admin/organization/projects/groups/groups.py index 8525a65255..aad9bfd6ec 100644 --- a/src/openai/resources/admin/organization/projects/groups/groups.py +++ b/src/openai/resources/admin/organization/projects/groups/groups.py @@ -20,7 +20,7 @@ from ......_compat import cached_property from ......_resource import SyncAPIResource, AsyncAPIResource from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ......pagination import SyncCursorPage, AsyncCursorPage +from ......pagination import SyncNextCursorPage, AsyncNextCursorPage from ......_base_client import AsyncPaginator, make_request_options from ......types.admin.organization.projects import group_list_params, group_create_params from ......types.admin.organization.projects.project_group import ProjectGroup @@ -116,7 +116,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[ProjectGroup]: + ) -> SyncNextCursorPage[ProjectGroup]: """ Lists the groups that have access to a project. @@ -140,7 +140,7 @@ def list( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/groups", project_id=project_id), - page=SyncCursorPage[ProjectGroup], + page=SyncNextCursorPage[ProjectGroup], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -289,7 +289,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[ProjectGroup, AsyncCursorPage[ProjectGroup]]: + ) -> AsyncPaginator[ProjectGroup, AsyncNextCursorPage[ProjectGroup]]: """ Lists the groups that have access to a project. @@ -313,7 +313,7 @@ def list( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/organization/projects/{project_id}/groups", project_id=project_id), - page=AsyncCursorPage[ProjectGroup], + page=AsyncNextCursorPage[ProjectGroup], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/projects/groups/roles.py b/src/openai/resources/admin/organization/projects/groups/roles.py index 20cab5d0e4..e3fe3b54fe 100644 --- a/src/openai/resources/admin/organization/projects/groups/roles.py +++ b/src/openai/resources/admin/organization/projects/groups/roles.py @@ -12,7 +12,7 @@ from ......_compat import cached_property from ......_resource import SyncAPIResource, AsyncAPIResource from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ......pagination import SyncCursorPage, AsyncCursorPage +from ......pagination import SyncNextCursorPage, AsyncNextCursorPage from ......_base_client import AsyncPaginator, make_request_options from ......types.admin.organization.projects.groups import role_list_params, role_create_params from ......types.admin.organization.projects.groups.role_list_response import RoleListResponse @@ -100,7 +100,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[RoleListResponse]: + ) -> SyncNextCursorPage[RoleListResponse]: """ Lists the project roles assigned to a group within a project. @@ -126,7 +126,7 @@ def list( raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return self._get_api_list( path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), - page=SyncCursorPage[RoleListResponse], + page=SyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -272,7 +272,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: """ Lists the project roles assigned to a group within a project. @@ -298,7 +298,7 @@ def list( raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return self._get_api_list( path_template("/projects/{project_id}/groups/{group_id}/roles", project_id=project_id, group_id=group_id), - page=AsyncCursorPage[RoleListResponse], + page=AsyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/projects/roles.py b/src/openai/resources/admin/organization/projects/roles.py index 4242afe052..c958b037bb 100644 --- a/src/openai/resources/admin/organization/projects/roles.py +++ b/src/openai/resources/admin/organization/projects/roles.py @@ -13,7 +13,7 @@ from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncCursorPage, AsyncCursorPage +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage from ....._base_client import AsyncPaginator, make_request_options from .....types.admin.organization.role import Role from .....types.admin.organization.projects import role_list_params, role_create_params, role_update_params @@ -166,7 +166,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[Role]: + ) -> SyncNextCursorPage[Role]: """Lists the roles configured for a project. Args: @@ -191,7 +191,7 @@ def list( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/projects/{project_id}/roles", project_id=project_id), - page=SyncCursorPage[Role], + page=SyncNextCursorPage[Role], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -395,7 +395,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Role, AsyncCursorPage[Role]]: + ) -> AsyncPaginator[Role, AsyncNextCursorPage[Role]]: """Lists the roles configured for a project. Args: @@ -420,7 +420,7 @@ def list( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._get_api_list( path_template("/projects/{project_id}/roles", project_id=project_id), - page=AsyncCursorPage[Role], + page=AsyncNextCursorPage[Role], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/projects/users/roles.py b/src/openai/resources/admin/organization/projects/users/roles.py index 35cde9b890..6a3233e275 100644 --- a/src/openai/resources/admin/organization/projects/users/roles.py +++ b/src/openai/resources/admin/organization/projects/users/roles.py @@ -12,7 +12,7 @@ from ......_compat import cached_property from ......_resource import SyncAPIResource, AsyncAPIResource from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ......pagination import SyncCursorPage, AsyncCursorPage +from ......pagination import SyncNextCursorPage, AsyncNextCursorPage from ......_base_client import AsyncPaginator, make_request_options from ......types.admin.organization.projects.users import role_list_params, role_create_params from ......types.admin.organization.projects.users.role_list_response import RoleListResponse @@ -100,7 +100,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[RoleListResponse]: + ) -> SyncNextCursorPage[RoleListResponse]: """ Lists the project roles assigned to a user within a project. @@ -126,7 +126,7 @@ def list( raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") return self._get_api_list( path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), - page=SyncCursorPage[RoleListResponse], + page=SyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -272,7 +272,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: """ Lists the project roles assigned to a user within a project. @@ -298,7 +298,7 @@ def list( raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") return self._get_api_list( path_template("/projects/{project_id}/users/{user_id}/roles", project_id=project_id, user_id=user_id), - page=AsyncCursorPage[RoleListResponse], + page=AsyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/roles.py b/src/openai/resources/admin/organization/roles.py index 3bfb35fd73..b25bbfb21e 100644 --- a/src/openai/resources/admin/organization/roles.py +++ b/src/openai/resources/admin/organization/roles.py @@ -13,7 +13,7 @@ from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncCursorPage, AsyncCursorPage +from ....pagination import SyncNextCursorPage, AsyncNextCursorPage from ...._base_client import AsyncPaginator, make_request_options from ....types.admin.organization import role_list_params, role_create_params, role_update_params from ....types.admin.organization.role import Role @@ -159,7 +159,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[Role]: + ) -> SyncNextCursorPage[Role]: """ Lists the roles configured for the organization. @@ -181,7 +181,7 @@ def list( """ return self._get_api_list( "/organization/roles", - page=SyncCursorPage[Role], + page=SyncNextCursorPage[Role], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -375,7 +375,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Role, AsyncCursorPage[Role]]: + ) -> AsyncPaginator[Role, AsyncNextCursorPage[Role]]: """ Lists the roles configured for the organization. @@ -397,7 +397,7 @@ def list( """ return self._get_api_list( "/organization/roles", - page=AsyncCursorPage[Role], + page=AsyncNextCursorPage[Role], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/users/roles.py b/src/openai/resources/admin/organization/users/roles.py index 4338d1ae1d..01ea5f4844 100644 --- a/src/openai/resources/admin/organization/users/roles.py +++ b/src/openai/resources/admin/organization/users/roles.py @@ -12,7 +12,7 @@ from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncCursorPage, AsyncCursorPage +from .....pagination import SyncNextCursorPage, AsyncNextCursorPage from ....._base_client import AsyncPaginator, make_request_options from .....types.admin.organization.users import role_list_params, role_create_params from .....types.admin.organization.users.role_list_response import RoleListResponse @@ -96,7 +96,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[RoleListResponse]: + ) -> SyncNextCursorPage[RoleListResponse]: """ Lists the organization roles assigned to a user within the organization. @@ -120,7 +120,7 @@ def list( raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") return self._get_api_list( path_template("/organization/users/{user_id}/roles", user_id=user_id), - page=SyncCursorPage[RoleListResponse], + page=SyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -254,7 +254,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[RoleListResponse, AsyncCursorPage[RoleListResponse]]: + ) -> AsyncPaginator[RoleListResponse, AsyncNextCursorPage[RoleListResponse]]: """ Lists the organization roles assigned to a user within the organization. @@ -278,7 +278,7 @@ def list( raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") return self._get_api_list( path_template("/organization/users/{user_id}/roles", user_id=user_id), - page=AsyncCursorPage[RoleListResponse], + page=AsyncNextCursorPage[RoleListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/users/users.py b/src/openai/resources/admin/organization/users/users.py index bfab2c5ecd..b65a138d1a 100644 --- a/src/openai/resources/admin/organization/users/users.py +++ b/src/openai/resources/admin/organization/users/users.py @@ -94,7 +94,7 @@ def update( self, user_id: str, *, - role: Literal["owner", "reader"], + role: Literal["owner", "reader"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -290,7 +290,7 @@ async def update( self, user_id: str, *, - role: Literal["owner", "reader"], + role: Literal["owner", "reader"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index 3fbd5cf1ac..dbb11795e5 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -39,6 +39,7 @@ from .usage_moderations_params import UsageModerationsParams as UsageModerationsParams from .admin_api_key_list_params import AdminAPIKeyListParams as AdminAPIKeyListParams from .certificate_create_params import CertificateCreateParams as CertificateCreateParams +from .certificate_list_response import CertificateListResponse as CertificateListResponse from .certificate_update_params import CertificateUpdateParams as CertificateUpdateParams from .usage_embeddings_response import UsageEmbeddingsResponse as UsageEmbeddingsResponse from .usage_completions_response import UsageCompletionsResponse as UsageCompletionsResponse @@ -50,9 +51,12 @@ from .certificate_retrieve_params import CertificateRetrieveParams as CertificateRetrieveParams from .usage_audio_speeches_params import UsageAudioSpeechesParams as UsageAudioSpeechesParams from .usage_vector_stores_response import UsageVectorStoresResponse as UsageVectorStoresResponse +from .admin_api_key_create_response import AdminAPIKeyCreateResponse as AdminAPIKeyCreateResponse from .admin_api_key_delete_response import AdminAPIKeyDeleteResponse as AdminAPIKeyDeleteResponse +from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse +from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse from .usage_code_interpreter_sessions_params import ( diff --git a/src/openai/types/admin/organization/admin_api_key.py b/src/openai/types/admin/organization/admin_api_key.py index 576d591d95..76951a99dd 100644 --- a/src/openai/types/admin/organization/admin_api_key.py +++ b/src/openai/types/admin/organization/admin_api_key.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal from ...._models import BaseModel @@ -42,7 +43,7 @@ class AdminAPIKey(BaseModel): name: str """The name of the API key""" - object: str + object: Literal["organization.admin_api_key"] """The object type, which is always `organization.admin_api_key`""" owner: Owner diff --git a/src/openai/types/admin/organization/admin_api_key_create_response.py b/src/openai/types/admin/organization/admin_api_key_create_response.py new file mode 100644 index 0000000000..48b2aaaaba --- /dev/null +++ b/src/openai/types/admin/organization/admin_api_key_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .admin_api_key import AdminAPIKey + +__all__ = ["AdminAPIKeyCreateResponse"] + + +class AdminAPIKeyCreateResponse(AdminAPIKey): + """Represents an individual Admin API key in an org.""" + + value: str # type: ignore + """The value of the API key. Only shown on create.""" diff --git a/src/openai/types/admin/organization/admin_api_key_delete_response.py b/src/openai/types/admin/organization/admin_api_key_delete_response.py index 7b4dab86a1..df8c5171d4 100644 --- a/src/openai/types/admin/organization/admin_api_key_delete_response.py +++ b/src/openai/types/admin/organization/admin_api_key_delete_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing_extensions import Literal from ...._models import BaseModel @@ -8,8 +8,8 @@ class AdminAPIKeyDeleteResponse(BaseModel): - id: Optional[str] = None + id: str - deleted: Optional[bool] = None + deleted: bool - object: Optional[str] = None + object: Literal["organization.admin_api_key.deleted"] diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py index db4d20b9ef..ec899758a2 100644 --- a/src/openai/types/admin/organization/audit_log_list_response.py +++ b/src/openai/types/admin/organization/audit_log_list_response.py @@ -810,9 +810,6 @@ class AuditLogListResponse(BaseModel): id: str """The ID of this log.""" - actor: Actor - """The actor who performed the audit logged action.""" - effective_at: int """The Unix timestamp (in seconds) of the event.""" @@ -871,6 +868,9 @@ class AuditLogListResponse(BaseModel): ] """The event type.""" + actor: Optional[Actor] = None + """The actor who performed the audit logged action.""" + api_key_created: Optional[APIKeyCreated] = FieldInfo(alias="api_key.created", default=None) """The details for events with this `type`.""" diff --git a/src/openai/types/admin/organization/certificate.py b/src/openai/types/admin/organization/certificate.py index 93347d816b..a8663b4e4a 100644 --- a/src/openai/types/admin/organization/certificate.py +++ b/src/openai/types/admin/organization/certificate.py @@ -30,7 +30,7 @@ class Certificate(BaseModel): created_at: int """The Unix timestamp (in seconds) of when the certificate was uploaded.""" - name: str + name: Optional[str] = None """The name of the certificate.""" object: Literal["certificate", "organization.certificate", "organization.project.certificate"] diff --git a/src/openai/types/admin/organization/certificate_activate_response.py b/src/openai/types/admin/organization/certificate_activate_response.py new file mode 100644 index 0000000000..64239c3a93 --- /dev/null +++ b/src/openai/types/admin/organization/certificate_activate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateActivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateActivateResponse(BaseModel): + """Represents an individual certificate configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the organization level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.certificate"] + """The object type, which is always `organization.certificate`.""" diff --git a/src/openai/types/admin/organization/certificate_create_params.py b/src/openai/types/admin/organization/certificate_create_params.py index 3af81f03cb..9aeb3bbc94 100644 --- a/src/openai/types/admin/organization/certificate_create_params.py +++ b/src/openai/types/admin/organization/certificate_create_params.py @@ -8,7 +8,7 @@ class CertificateCreateParams(TypedDict, total=False): - content: Required[str] + certificate: Required[str] """The certificate content in PEM format""" name: str diff --git a/src/openai/types/admin/organization/certificate_deactivate_response.py b/src/openai/types/admin/organization/certificate_deactivate_response.py new file mode 100644 index 0000000000..874251cadc --- /dev/null +++ b/src/openai/types/admin/organization/certificate_deactivate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateDeactivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateDeactivateResponse(BaseModel): + """Represents an individual certificate configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the organization level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.certificate"] + """The object type, which is always `organization.certificate`.""" diff --git a/src/openai/types/admin/organization/certificate_list_response.py b/src/openai/types/admin/organization/certificate_list_response.py new file mode 100644 index 0000000000..8d9816520f --- /dev/null +++ b/src/openai/types/admin/organization/certificate_list_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["CertificateListResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateListResponse(BaseModel): + """Represents an individual certificate configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the organization level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.certificate"] + """The object type, which is always `organization.certificate`.""" diff --git a/src/openai/types/admin/organization/certificate_update_params.py b/src/openai/types/admin/organization/certificate_update_params.py index 8a048b52b9..c55b3aceb2 100644 --- a/src/openai/types/admin/organization/certificate_update_params.py +++ b/src/openai/types/admin/organization/certificate_update_params.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import TypedDict __all__ = ["CertificateUpdateParams"] class CertificateUpdateParams(TypedDict, total=False): - name: Required[str] + name: str """The updated name for the certificate""" diff --git a/src/openai/types/admin/organization/groups/__init__.py b/src/openai/types/admin/organization/groups/__init__.py index a1af586634..22189c1085 100644 --- a/src/openai/types/admin/organization/groups/__init__.py +++ b/src/openai/types/admin/organization/groups/__init__.py @@ -11,3 +11,4 @@ from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse from .user_create_response import UserCreateResponse as UserCreateResponse from .user_delete_response import UserDeleteResponse as UserDeleteResponse +from .organization_group_user import OrganizationGroupUser as OrganizationGroupUser diff --git a/src/openai/types/admin/organization/groups/organization_group_user.py b/src/openai/types/admin/organization/groups/organization_group_user.py new file mode 100644 index 0000000000..792022c4a9 --- /dev/null +++ b/src/openai/types/admin/organization/groups/organization_group_user.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ....._models import BaseModel + +__all__ = ["OrganizationGroupUser"] + + +class OrganizationGroupUser(BaseModel): + """Represents an individual user returned when inspecting group membership.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + email: Optional[str] = None + """The email address of the user.""" + + name: str + """The name of the user.""" diff --git a/src/openai/types/admin/organization/invite.py b/src/openai/types/admin/organization/invite.py index 5c051500b9..dc72dc2b5b 100644 --- a/src/openai/types/admin/organization/invite.py +++ b/src/openai/types/admin/organization/invite.py @@ -22,15 +22,15 @@ class Invite(BaseModel): id: str """The identifier, which can be referenced in API endpoints""" + created_at: int + """The Unix timestamp (in seconds) of when the invite was sent.""" + email: str """The email address of the individual to whom the invite was sent""" - expires_at: int + expires_at: Optional[int] = None """The Unix timestamp (in seconds) of when the invite expires.""" - invited_at: int - """The Unix timestamp (in seconds) of when the invite was sent.""" - object: Literal["organization.invite"] """The object type, which is always `organization.invite`""" diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py index 8af54d8659..ea627ce7d6 100644 --- a/src/openai/types/admin/organization/projects/__init__.py +++ b/src/openai/types/admin/organization/projects/__init__.py @@ -21,10 +21,13 @@ from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse from .certificate_list_params import CertificateListParams as CertificateListParams from .project_service_account import ProjectServiceAccount as ProjectServiceAccount +from .certificate_list_response import CertificateListResponse as CertificateListResponse from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams +from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams +from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse from .service_account_create_response import ServiceAccountCreateResponse as ServiceAccountCreateResponse from .service_account_delete_response import ServiceAccountDeleteResponse as ServiceAccountDeleteResponse from .rate_limit_list_rate_limits_params import RateLimitListRateLimitsParams as RateLimitListRateLimitsParams diff --git a/src/openai/types/admin/organization/projects/certificate_activate_response.py b/src/openai/types/admin/organization/projects/certificate_activate_response.py new file mode 100644 index 0000000000..4ee8ae07f4 --- /dev/null +++ b/src/openai/types/admin/organization/projects/certificate_activate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CertificateActivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateActivateResponse(BaseModel): + """Represents an individual certificate configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the project level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.project.certificate"] + """The object type, which is always `organization.project.certificate`.""" diff --git a/src/openai/types/admin/organization/projects/certificate_deactivate_response.py b/src/openai/types/admin/organization/projects/certificate_deactivate_response.py new file mode 100644 index 0000000000..846c7a2ab7 --- /dev/null +++ b/src/openai/types/admin/organization/projects/certificate_deactivate_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CertificateDeactivateResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateDeactivateResponse(BaseModel): + """Represents an individual certificate configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the project level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.project.certificate"] + """The object type, which is always `organization.project.certificate`.""" diff --git a/src/openai/types/admin/organization/projects/certificate_list_response.py b/src/openai/types/admin/organization/projects/certificate_list_response.py new file mode 100644 index 0000000000..d4345b3b8c --- /dev/null +++ b/src/openai/types/admin/organization/projects/certificate_list_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["CertificateListResponse", "CertificateDetails"] + + +class CertificateDetails(BaseModel): + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate expires.""" + + valid_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the certificate becomes valid.""" + + +class CertificateListResponse(BaseModel): + """Represents an individual certificate configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + active: bool + """Whether the certificate is currently active at the project level.""" + + certificate_details: CertificateDetails + + created_at: int + """The Unix timestamp (in seconds) of when the certificate was uploaded.""" + + name: Optional[str] = None + """The name of the certificate.""" + + object: Literal["organization.project.certificate"] + """The object type, which is always `organization.project.certificate`.""" diff --git a/src/openai/types/admin/organization/projects/project_api_key.py b/src/openai/types/admin/organization/projects/project_api_key.py index 69cb243ba3..7e5e6949eb 100644 --- a/src/openai/types/admin/organization/projects/project_api_key.py +++ b/src/openai/types/admin/organization/projects/project_api_key.py @@ -4,21 +4,54 @@ from typing_extensions import Literal from ....._models import BaseModel -from .project_user import ProjectUser -from .project_service_account import ProjectServiceAccount -__all__ = ["ProjectAPIKey", "Owner"] +__all__ = ["ProjectAPIKey", "Owner", "OwnerServiceAccount", "OwnerUser"] + + +class OwnerServiceAccount(BaseModel): + """The service account that owns a project API key.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the service account was created.""" + + name: str + """The name of the service account.""" + + role: str + """The service account's project role.""" + + +class OwnerUser(BaseModel): + """The user that owns a project API key.""" + + id: str + """The identifier, which can be referenced in API endpoints""" + + created_at: int + """The Unix timestamp (in seconds) of when the user was created.""" + + email: str + """The email address of the user.""" + + name: str + """The name of the user.""" + + role: str + """The user's project role.""" class Owner(BaseModel): - service_account: Optional[ProjectServiceAccount] = None - """Represents an individual service account in a project.""" + service_account: Optional[OwnerServiceAccount] = None + """The service account that owns a project API key.""" type: Optional[Literal["user", "service_account"]] = None """`user` or `service_account`""" - user: Optional[ProjectUser] = None - """Represents an individual user in a project.""" + user: Optional[OwnerUser] = None + """The user that owns a project API key.""" class ProjectAPIKey(BaseModel): @@ -30,7 +63,7 @@ class ProjectAPIKey(BaseModel): created_at: int """The Unix timestamp (in seconds) of when the API key was created""" - last_used_at: int + last_used_at: Optional[int] = None """The Unix timestamp (in seconds) of when the API key was last used.""" name: str diff --git a/src/openai/types/admin/organization/projects/service_account_create_response.py b/src/openai/types/admin/organization/projects/service_account_create_response.py index ce9cfa5530..430b11f655 100644 --- a/src/openai/types/admin/organization/projects/service_account_create_response.py +++ b/src/openai/types/admin/organization/projects/service_account_create_response.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ....._models import BaseModel @@ -23,7 +24,7 @@ class APIKey(BaseModel): class ServiceAccountCreateResponse(BaseModel): id: str - api_key: APIKey + api_key: Optional[APIKey] = None created_at: int diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py index f41ecad30d..90e17c89b0 100644 --- a/src/openai/types/admin/organization/usage_audio_speeches_response.py +++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageAudioSpeechesResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py index 2d847dd9ae..abd7c3fb90 100644 --- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageAudioTranscriptionsResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py index e4634d9a6f..0cd6c2693c 100644 --- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py +++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageCodeInterpreterSessionsResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py index a79c79c9bb..d37634bca5 100644 --- a/src/openai/types/admin/organization/usage_completions_response.py +++ b/src/openai/types/admin/organization/usage_completions_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageCompletionsResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py index 004e818242..68c1f639c1 100644 --- a/src/openai/types/admin/organization/usage_costs_response.py +++ b/src/openai/types/admin/organization/usage_costs_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageCostsResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py index f14208507c..905c8f5c6e 100644 --- a/src/openai/types/admin/organization/usage_embeddings_response.py +++ b/src/openai/types/admin/organization/usage_embeddings_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageEmbeddingsResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py index 0188a51bc4..55f8d80096 100644 --- a/src/openai/types/admin/organization/usage_images_response.py +++ b/src/openai/types/admin/organization/usage_images_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageImagesResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py index fc99593e5d..87919b50a9 100644 --- a/src/openai/types/admin/organization/usage_moderations_response.py +++ b/src/openai/types/admin/organization/usage_moderations_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageModerationsResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py index f17e867af0..d3cd853653 100644 --- a/src/openai/types/admin/organization/usage_vector_stores_response.py +++ b/src/openai/types/admin/organization/usage_vector_stores_response.py @@ -305,11 +305,11 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): The aggregated code interpreter sessions usage details of the specific time bucket. """ - object: Literal["organization.usage.code_interpreter_sessions.result"] - - num_sessions: Optional[int] = None + num_sessions: int """The number of code interpreter sessions.""" + object: Literal["organization.usage.code_interpreter_sessions.result"] + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped @@ -375,7 +375,7 @@ class Data(BaseModel): object: Literal["bucket"] - result: List[DataResult] + results: List[DataResult] start_time: int @@ -385,6 +385,6 @@ class UsageVectorStoresResponse(BaseModel): has_more: bool - next_page: str + next_page: Optional[str] = None object: Literal["page"] diff --git a/src/openai/types/admin/organization/user_update_params.py b/src/openai/types/admin/organization/user_update_params.py index bc276120e3..ab1f9d0026 100644 --- a/src/openai/types/admin/organization/user_update_params.py +++ b/src/openai/types/admin/organization/user_update_params.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, TypedDict __all__ = ["UserUpdateParams"] class UserUpdateParams(TypedDict, total=False): - role: Required[Literal["owner", "reader"]] + role: Literal["owner", "reader"] """`owner` or `reader`""" diff --git a/tests/api_resources/admin/organization/groups/test_roles.py b/tests/api_resources/admin/organization/groups/test_roles.py index 73e8feabdb..08702fddd2 100644 --- a/tests/api_resources/admin/organization/groups/test_roles.py +++ b/tests/api_resources/admin/organization/groups/test_roles.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization.groups import ( RoleListResponse, RoleCreateResponse, @@ -69,7 +69,7 @@ def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.groups.roles.list( group_id="group_id", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -79,7 +79,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -90,7 +90,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -101,7 +101,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True @@ -213,7 +213,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.groups.roles.list( group_id="group_id", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -223,7 +223,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -234,7 +234,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -245,7 +245,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = await response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/groups/test_users.py b/tests/api_resources/admin/organization/groups/test_users.py index cfa6354f99..eda6be6bbf 100644 --- a/tests/api_resources/admin/organization/groups/test_users.py +++ b/tests/api_resources/admin/organization/groups/test_users.py @@ -9,11 +9,11 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage -from openai.types.admin.organization import OrganizationUser +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization.groups import ( UserCreateResponse, UserDeleteResponse, + OrganizationGroupUser, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -69,7 +69,7 @@ def test_method_list(self, client: OpenAI) -> None: user = client.admin.organization.groups.users.list( group_id="group_id", ) - assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(SyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -79,7 +79,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(SyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -90,7 +90,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() - assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(SyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -101,7 +101,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() - assert_matches_type(SyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(SyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) assert cast(Any, response.is_closed) is True @@ -213,7 +213,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: user = await async_client.admin.organization.groups.users.list( group_id="group_id", ) - assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(AsyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -223,7 +223,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(AsyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -234,7 +234,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = response.parse() - assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(AsyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -245,7 +245,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" user = await response.parse() - assert_matches_type(AsyncCursorPage[OrganizationUser], user, path=["response"]) + assert_matches_type(AsyncNextCursorPage[OrganizationGroupUser], user, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/projects/groups/test_roles.py b/tests/api_resources/admin/organization/projects/groups/test_roles.py index 4cfc957962..a129142ea9 100644 --- a/tests/api_resources/admin/organization/projects/groups/test_roles.py +++ b/tests/api_resources/admin/organization/projects/groups/test_roles.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization.projects.groups import ( RoleListResponse, RoleCreateResponse, @@ -81,7 +81,7 @@ def test_method_list(self, client: OpenAI) -> None: group_id="group_id", project_id="project_id", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -92,7 +92,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -104,7 +104,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -116,7 +116,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True @@ -259,7 +259,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: group_id="group_id", project_id="project_id", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -270,7 +270,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -282,7 +282,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -294,7 +294,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = await response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/projects/test_api_keys.py b/tests/api_resources/admin/organization/projects/test_api_keys.py index 2f8ab56056..f9aef44216 100644 --- a/tests/api_resources/admin/organization/projects/test_api_keys.py +++ b/tests/api_resources/admin/organization/projects/test_api_keys.py @@ -21,7 +21,7 @@ class TestAPIKeys: @parametrize def test_method_retrieve(self, client: OpenAI) -> None: api_key = client.admin.organization.projects.api_keys.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) assert_matches_type(ProjectAPIKey, api_key, path=["response"]) @@ -29,7 +29,7 @@ def test_method_retrieve(self, client: OpenAI) -> None: @parametrize def test_raw_response_retrieve(self, client: OpenAI) -> None: response = client.admin.organization.projects.api_keys.with_raw_response.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) @@ -41,7 +41,7 @@ def test_raw_response_retrieve(self, client: OpenAI) -> None: @parametrize def test_streaming_response_retrieve(self, client: OpenAI) -> None: with client.admin.organization.projects.api_keys.with_streaming_response.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) as response: assert not response.is_closed @@ -56,13 +56,13 @@ def test_streaming_response_retrieve(self, client: OpenAI) -> None: def test_path_params_retrieve(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): client.admin.organization.projects.api_keys.with_raw_response.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="", ) - with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"): client.admin.organization.projects.api_keys.with_raw_response.retrieve( - key_id="", + api_key_id="", project_id="project_id", ) @@ -116,7 +116,7 @@ def test_path_params_list(self, client: OpenAI) -> None: @parametrize def test_method_delete(self, client: OpenAI) -> None: api_key = client.admin.organization.projects.api_keys.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) @@ -124,7 +124,7 @@ def test_method_delete(self, client: OpenAI) -> None: @parametrize def test_raw_response_delete(self, client: OpenAI) -> None: response = client.admin.organization.projects.api_keys.with_raw_response.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) @@ -136,7 +136,7 @@ def test_raw_response_delete(self, client: OpenAI) -> None: @parametrize def test_streaming_response_delete(self, client: OpenAI) -> None: with client.admin.organization.projects.api_keys.with_streaming_response.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) as response: assert not response.is_closed @@ -151,13 +151,13 @@ def test_streaming_response_delete(self, client: OpenAI) -> None: def test_path_params_delete(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): client.admin.organization.projects.api_keys.with_raw_response.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="", ) - with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"): client.admin.organization.projects.api_keys.with_raw_response.delete( - key_id="", + api_key_id="", project_id="project_id", ) @@ -170,7 +170,7 @@ class TestAsyncAPIKeys: @parametrize async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: api_key = await async_client.admin.organization.projects.api_keys.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) assert_matches_type(ProjectAPIKey, api_key, path=["response"]) @@ -178,7 +178,7 @@ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.projects.api_keys.with_raw_response.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) @@ -190,7 +190,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: async with async_client.admin.organization.projects.api_keys.with_streaming_response.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) as response: assert not response.is_closed @@ -205,13 +205,13 @@ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> N async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): await async_client.admin.organization.projects.api_keys.with_raw_response.retrieve( - key_id="key_id", + api_key_id="api_key_id", project_id="", ) - with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"): await async_client.admin.organization.projects.api_keys.with_raw_response.retrieve( - key_id="", + api_key_id="", project_id="project_id", ) @@ -265,7 +265,7 @@ async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_delete(self, async_client: AsyncOpenAI) -> None: api_key = await async_client.admin.organization.projects.api_keys.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"]) @@ -273,7 +273,7 @@ async def test_method_delete(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.projects.api_keys.with_raw_response.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) @@ -285,7 +285,7 @@ async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: async with async_client.admin.organization.projects.api_keys.with_streaming_response.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="project_id", ) as response: assert not response.is_closed @@ -300,12 +300,12 @@ async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> Non async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): await async_client.admin.organization.projects.api_keys.with_raw_response.delete( - key_id="key_id", + api_key_id="api_key_id", project_id="", ) - with pytest.raises(ValueError, match=r"Expected a non-empty value for `key_id` but received ''"): + with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"): await async_client.admin.organization.projects.api_keys.with_raw_response.delete( - key_id="", + api_key_id="", project_id="project_id", ) diff --git a/tests/api_resources/admin/organization/projects/test_certificates.py b/tests/api_resources/admin/organization/projects/test_certificates.py index 8ed72d6112..e242b7d6d4 100644 --- a/tests/api_resources/admin/organization/projects/test_certificates.py +++ b/tests/api_resources/admin/organization/projects/test_certificates.py @@ -10,7 +10,11 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type from openai.pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage -from openai.types.admin.organization import Certificate +from openai.types.admin.organization.projects import ( + CertificateListResponse, + CertificateActivateResponse, + CertificateDeactivateResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -23,7 +27,7 @@ def test_method_list(self, client: OpenAI) -> None: certificate = client.admin.organization.projects.certificates.list( project_id="project_id", ) - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -33,7 +37,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -44,7 +48,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -55,7 +59,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -72,7 +76,7 @@ def test_method_activate(self, client: OpenAI) -> None: project_id="project_id", certificate_ids=["cert_abc"], ) - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize def test_raw_response_activate(self, client: OpenAI) -> None: @@ -84,7 +88,7 @@ def test_raw_response_activate(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize def test_streaming_response_activate(self, client: OpenAI) -> None: @@ -96,7 +100,7 @@ def test_streaming_response_activate(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateActivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -114,7 +118,7 @@ def test_method_deactivate(self, client: OpenAI) -> None: project_id="project_id", certificate_ids=["cert_abc"], ) - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize def test_raw_response_deactivate(self, client: OpenAI) -> None: @@ -126,7 +130,7 @@ def test_raw_response_deactivate(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize def test_streaming_response_deactivate(self, client: OpenAI) -> None: @@ -138,7 +142,7 @@ def test_streaming_response_deactivate(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateDeactivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -161,7 +165,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: certificate = await async_client.admin.organization.projects.certificates.list( project_id="project_id", ) - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -171,7 +175,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -182,7 +186,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -193,7 +197,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -210,7 +214,7 @@ async def test_method_activate(self, async_client: AsyncOpenAI) -> None: project_id="project_id", certificate_ids=["cert_abc"], ) - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize async def test_raw_response_activate(self, async_client: AsyncOpenAI) -> None: @@ -222,7 +226,7 @@ async def test_raw_response_activate(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize async def test_streaming_response_activate(self, async_client: AsyncOpenAI) -> None: @@ -234,7 +238,7 @@ async def test_streaming_response_activate(self, async_client: AsyncOpenAI) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateActivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -252,7 +256,7 @@ async def test_method_deactivate(self, async_client: AsyncOpenAI) -> None: project_id="project_id", certificate_ids=["cert_abc"], ) - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize async def test_raw_response_deactivate(self, async_client: AsyncOpenAI) -> None: @@ -264,7 +268,7 @@ async def test_raw_response_deactivate(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize async def test_streaming_response_deactivate(self, async_client: AsyncOpenAI) -> None: @@ -276,7 +280,7 @@ async def test_streaming_response_deactivate(self, async_client: AsyncOpenAI) -> assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateDeactivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/projects/test_groups.py b/tests/api_resources/admin/organization/projects/test_groups.py index 57d768e738..2db448e9b2 100644 --- a/tests/api_resources/admin/organization/projects/test_groups.py +++ b/tests/api_resources/admin/organization/projects/test_groups.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization.projects import ( ProjectGroup, GroupDeleteResponse, @@ -72,7 +72,7 @@ def test_method_list(self, client: OpenAI) -> None: group = client.admin.organization.projects.groups.list( project_id="project_id", ) - assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[ProjectGroup], group, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -82,7 +82,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[ProjectGroup], group, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -93,7 +93,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = response.parse() - assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[ProjectGroup], group, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -104,7 +104,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = response.parse() - assert_matches_type(SyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[ProjectGroup], group, path=["response"]) assert cast(Any, response.is_closed) is True @@ -220,7 +220,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: group = await async_client.admin.organization.projects.groups.list( project_id="project_id", ) - assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[ProjectGroup], group, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -230,7 +230,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[ProjectGroup], group, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -241,7 +241,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = response.parse() - assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[ProjectGroup], group, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -252,7 +252,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = await response.parse() - assert_matches_type(AsyncCursorPage[ProjectGroup], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[ProjectGroup], group, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/projects/test_roles.py b/tests/api_resources/admin/organization/projects/test_roles.py index 697aa09f3a..8c78ea21b4 100644 --- a/tests/api_resources/admin/organization/projects/test_roles.py +++ b/tests/api_resources/admin/organization/projects/test_roles.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization import Role from openai.types.admin.organization.projects import ( RoleDeleteResponse, @@ -141,7 +141,7 @@ def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.projects.roles.list( project_id="project_id", ) - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -151,7 +151,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -162,7 +162,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -173,7 +173,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) assert cast(Any, response.is_closed) is True @@ -358,7 +358,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.projects.roles.list( project_id="project_id", ) - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -368,7 +368,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -379,7 +379,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -390,7 +390,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = await response.parse() - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/projects/users/test_roles.py b/tests/api_resources/admin/organization/projects/users/test_roles.py index c39840646b..99c52d494c 100644 --- a/tests/api_resources/admin/organization/projects/users/test_roles.py +++ b/tests/api_resources/admin/organization/projects/users/test_roles.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization.projects.users import ( RoleListResponse, RoleCreateResponse, @@ -81,7 +81,7 @@ def test_method_list(self, client: OpenAI) -> None: user_id="user_id", project_id="project_id", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -92,7 +92,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -104,7 +104,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -116,7 +116,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True @@ -259,7 +259,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: user_id="user_id", project_id="project_id", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -270,7 +270,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -282,7 +282,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -294,7 +294,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = await response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_admin_api_keys.py b/tests/api_resources/admin/organization/test_admin_api_keys.py index 3b6e7dec37..59eed910e8 100644 --- a/tests/api_resources/admin/organization/test_admin_api_keys.py +++ b/tests/api_resources/admin/organization/test_admin_api_keys.py @@ -12,6 +12,7 @@ from openai.pagination import SyncCursorPage, AsyncCursorPage from openai.types.admin.organization import ( AdminAPIKey, + AdminAPIKeyCreateResponse, AdminAPIKeyDeleteResponse, ) @@ -26,7 +27,7 @@ def test_method_create(self, client: OpenAI) -> None: admin_api_key = client.admin.organization.admin_api_keys.create( name="New Admin Key", ) - assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) @parametrize def test_raw_response_create(self, client: OpenAI) -> None: @@ -37,7 +38,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" admin_api_key = response.parse() - assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: @@ -48,7 +49,7 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" admin_api_key = response.parse() - assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) assert cast(Any, response.is_closed) is True @@ -173,7 +174,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: admin_api_key = await async_client.admin.organization.admin_api_keys.create( name="New Admin Key", ) - assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @@ -184,7 +185,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" admin_api_key = response.parse() - assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: @@ -195,7 +196,7 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert response.http_request.headers.get("X-Stainless-Lang") == "python" admin_api_key = await response.parse() - assert_matches_type(AdminAPIKey, admin_api_key, path=["response"]) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_certificates.py b/tests/api_resources/admin/organization/test_certificates.py index a1f0053f13..209fd9220d 100644 --- a/tests/api_resources/admin/organization/test_certificates.py +++ b/tests/api_resources/admin/organization/test_certificates.py @@ -12,7 +12,10 @@ from openai.pagination import SyncPage, AsyncPage, SyncConversationCursorPage, AsyncConversationCursorPage from openai.types.admin.organization import ( Certificate, + CertificateListResponse, CertificateDeleteResponse, + CertificateActivateResponse, + CertificateDeactivateResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -24,14 +27,14 @@ class TestCertificates: @parametrize def test_method_create(self, client: OpenAI) -> None: certificate = client.admin.organization.certificates.create( - content="content", + certificate="certificate", ) assert_matches_type(Certificate, certificate, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: OpenAI) -> None: certificate = client.admin.organization.certificates.create( - content="content", + certificate="certificate", name="name", ) assert_matches_type(Certificate, certificate, path=["response"]) @@ -39,7 +42,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.admin.organization.certificates.with_raw_response.create( - content="content", + certificate="certificate", ) assert response.is_closed is True @@ -50,7 +53,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: @parametrize def test_streaming_response_create(self, client: OpenAI) -> None: with client.admin.organization.certificates.with_streaming_response.create( - content="content", + certificate="certificate", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -108,6 +111,13 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: @parametrize def test_method_update(self, client: OpenAI) -> None: + certificate = client.admin.organization.certificates.update( + certificate_id="certificate_id", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: certificate = client.admin.organization.certificates.update( certificate_id="certificate_id", name="name", @@ -118,7 +128,6 @@ def test_method_update(self, client: OpenAI) -> None: def test_raw_response_update(self, client: OpenAI) -> None: response = client.admin.organization.certificates.with_raw_response.update( certificate_id="certificate_id", - name="name", ) assert response.is_closed is True @@ -130,7 +139,6 @@ def test_raw_response_update(self, client: OpenAI) -> None: def test_streaming_response_update(self, client: OpenAI) -> None: with client.admin.organization.certificates.with_streaming_response.update( certificate_id="certificate_id", - name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -145,13 +153,12 @@ def test_path_params_update(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): client.admin.organization.certificates.with_raw_response.update( certificate_id="", - name="name", ) @parametrize def test_method_list(self, client: OpenAI) -> None: certificate = client.admin.organization.certificates.list() - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -160,7 +167,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -169,7 +176,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -178,7 +185,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -225,7 +232,7 @@ def test_method_activate(self, client: OpenAI) -> None: certificate = client.admin.organization.certificates.activate( certificate_ids=["cert_abc"], ) - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize def test_raw_response_activate(self, client: OpenAI) -> None: @@ -236,7 +243,7 @@ def test_raw_response_activate(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize def test_streaming_response_activate(self, client: OpenAI) -> None: @@ -247,7 +254,7 @@ def test_streaming_response_activate(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateActivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -256,7 +263,7 @@ def test_method_deactivate(self, client: OpenAI) -> None: certificate = client.admin.organization.certificates.deactivate( certificate_ids=["cert_abc"], ) - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize def test_raw_response_deactivate(self, client: OpenAI) -> None: @@ -267,7 +274,7 @@ def test_raw_response_deactivate(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize def test_streaming_response_deactivate(self, client: OpenAI) -> None: @@ -278,7 +285,7 @@ def test_streaming_response_deactivate(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(SyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(SyncPage[CertificateDeactivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -291,14 +298,14 @@ class TestAsyncCertificates: @parametrize async def test_method_create(self, async_client: AsyncOpenAI) -> None: certificate = await async_client.admin.organization.certificates.create( - content="content", + certificate="certificate", ) assert_matches_type(Certificate, certificate, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: certificate = await async_client.admin.organization.certificates.create( - content="content", + certificate="certificate", name="name", ) assert_matches_type(Certificate, certificate, path=["response"]) @@ -306,7 +313,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.certificates.with_raw_response.create( - content="content", + certificate="certificate", ) assert response.is_closed is True @@ -317,7 +324,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.admin.organization.certificates.with_streaming_response.create( - content="content", + certificate="certificate", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -375,6 +382,13 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: + certificate = await async_client.admin.organization.certificates.update( + certificate_id="certificate_id", + ) + assert_matches_type(Certificate, certificate, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: certificate = await async_client.admin.organization.certificates.update( certificate_id="certificate_id", name="name", @@ -385,7 +399,6 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.certificates.with_raw_response.update( certificate_id="certificate_id", - name="name", ) assert response.is_closed is True @@ -397,7 +410,6 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: async with async_client.admin.organization.certificates.with_streaming_response.update( certificate_id="certificate_id", - name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -412,13 +424,12 @@ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): await async_client.admin.organization.certificates.with_raw_response.update( certificate_id="", - name="name", ) @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: certificate = await async_client.admin.organization.certificates.list() - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -427,7 +438,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -436,7 +447,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -445,7 +456,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() - assert_matches_type(AsyncConversationCursorPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncConversationCursorPage[CertificateListResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -492,7 +503,7 @@ async def test_method_activate(self, async_client: AsyncOpenAI) -> None: certificate = await async_client.admin.organization.certificates.activate( certificate_ids=["cert_abc"], ) - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize async def test_raw_response_activate(self, async_client: AsyncOpenAI) -> None: @@ -503,7 +514,7 @@ async def test_raw_response_activate(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateActivateResponse], certificate, path=["response"]) @parametrize async def test_streaming_response_activate(self, async_client: AsyncOpenAI) -> None: @@ -514,7 +525,7 @@ async def test_streaming_response_activate(self, async_client: AsyncOpenAI) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateActivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @@ -523,7 +534,7 @@ async def test_method_deactivate(self, async_client: AsyncOpenAI) -> None: certificate = await async_client.admin.organization.certificates.deactivate( certificate_ids=["cert_abc"], ) - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize async def test_raw_response_deactivate(self, async_client: AsyncOpenAI) -> None: @@ -534,7 +545,7 @@ async def test_raw_response_deactivate(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateDeactivateResponse], certificate, path=["response"]) @parametrize async def test_streaming_response_deactivate(self, async_client: AsyncOpenAI) -> None: @@ -545,6 +556,6 @@ async def test_streaming_response_deactivate(self, async_client: AsyncOpenAI) -> assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() - assert_matches_type(AsyncPage[Certificate], certificate, path=["response"]) + assert_matches_type(AsyncPage[CertificateDeactivateResponse], certificate, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_groups.py b/tests/api_resources/admin/organization/test_groups.py index b37469625b..0eca89f06c 100644 --- a/tests/api_resources/admin/organization/test_groups.py +++ b/tests/api_resources/admin/organization/test_groups.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization import ( Group, GroupDeleteResponse, @@ -98,7 +98,7 @@ def test_path_params_update(self, client: OpenAI) -> None: @parametrize def test_method_list(self, client: OpenAI) -> None: group = client.admin.organization.groups.list() - assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[Group], group, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -107,7 +107,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[Group], group, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -116,7 +116,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = response.parse() - assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[Group], group, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -125,7 +125,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = response.parse() - assert_matches_type(SyncCursorPage[Group], group, path=["response"]) + assert_matches_type(SyncNextCursorPage[Group], group, path=["response"]) assert cast(Any, response.is_closed) is True @@ -249,7 +249,7 @@ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: group = await async_client.admin.organization.groups.list() - assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Group], group, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -258,7 +258,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Group], group, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -267,7 +267,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = response.parse() - assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Group], group, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -276,7 +276,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" group = await response.parse() - assert_matches_type(AsyncCursorPage[Group], group, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Group], group, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_roles.py b/tests/api_resources/admin/organization/test_roles.py index 1b4b44278b..ee70020c23 100644 --- a/tests/api_resources/admin/organization/test_roles.py +++ b/tests/api_resources/admin/organization/test_roles.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization import ( Role, RoleDeleteResponse, @@ -115,7 +115,7 @@ def test_path_params_update(self, client: OpenAI) -> None: @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.roles.list() - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -124,7 +124,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -133,7 +133,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -142,7 +142,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[Role], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[Role], role, path=["response"]) assert cast(Any, response.is_closed) is True @@ -284,7 +284,7 @@ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.roles.list() - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -293,7 +293,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -302,7 +302,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -311,7 +311,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = await response.parse() - assert_matches_type(AsyncCursorPage[Role], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[Role], role, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_users.py b/tests/api_resources/admin/organization/test_users.py index 7350b6ecf5..8107bc5e53 100644 --- a/tests/api_resources/admin/organization/test_users.py +++ b/tests/api_resources/admin/organization/test_users.py @@ -58,6 +58,13 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: @parametrize def test_method_update(self, client: OpenAI) -> None: + user = client.admin.organization.users.update( + user_id="user_id", + ) + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: user = client.admin.organization.users.update( user_id="user_id", role="owner", @@ -68,7 +75,6 @@ def test_method_update(self, client: OpenAI) -> None: def test_raw_response_update(self, client: OpenAI) -> None: response = client.admin.organization.users.with_raw_response.update( user_id="user_id", - role="owner", ) assert response.is_closed is True @@ -80,7 +86,6 @@ def test_raw_response_update(self, client: OpenAI) -> None: def test_streaming_response_update(self, client: OpenAI) -> None: with client.admin.organization.users.with_streaming_response.update( user_id="user_id", - role="owner", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -95,7 +100,6 @@ def test_path_params_update(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): client.admin.organization.users.with_raw_response.update( user_id="", - role="owner", ) @parametrize @@ -216,6 +220,13 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.users.update( + user_id="user_id", + ) + assert_matches_type(OrganizationUser, user, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: user = await async_client.admin.organization.users.update( user_id="user_id", role="owner", @@ -226,7 +237,6 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.users.with_raw_response.update( user_id="user_id", - role="owner", ) assert response.is_closed is True @@ -238,7 +248,6 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: async with async_client.admin.organization.users.with_streaming_response.update( user_id="user_id", - role="owner", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -253,7 +262,6 @@ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): await async_client.admin.organization.users.with_raw_response.update( user_id="", - role="owner", ) @parametrize diff --git a/tests/api_resources/admin/organization/users/test_roles.py b/tests/api_resources/admin/organization/users/test_roles.py index 272c0459db..2455a38cff 100644 --- a/tests/api_resources/admin/organization/users/test_roles.py +++ b/tests/api_resources/admin/organization/users/test_roles.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.utils import assert_matches_type -from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.pagination import SyncNextCursorPage, AsyncNextCursorPage from openai.types.admin.organization.users import ( RoleListResponse, RoleCreateResponse, @@ -69,7 +69,7 @@ def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.users.roles.list( user_id="user_id", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: OpenAI) -> None: @@ -79,7 +79,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, order="asc", ) - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_raw_response_list(self, client: OpenAI) -> None: @@ -90,7 +90,7 @@ def test_raw_response_list(self, client: OpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize def test_streaming_response_list(self, client: OpenAI) -> None: @@ -101,7 +101,7 @@ def test_streaming_response_list(self, client: OpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(SyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(SyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True @@ -213,7 +213,7 @@ async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.users.roles.list( user_id="user_id", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: @@ -223,7 +223,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, order="asc", ) - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: @@ -234,7 +234,7 @@ async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: @@ -245,7 +245,7 @@ async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" role = await response.parse() - assert_matches_type(AsyncCursorPage[RoleListResponse], role, path=["response"]) + assert_matches_type(AsyncNextCursorPage[RoleListResponse], role, path=["response"]) assert cast(Any, response.is_closed) is True From a4c01ec71e19af034af09ab6fa00bac693f8d66a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 21:19:22 +0000 Subject: [PATCH 331/408] feat(api): add external_key_id to projects, email/metadata params to users, update types --- .stats.yml | 4 +- .../admin/organization/projects/projects.py | 48 +++++++++-- .../organization/projects/users/users.py | 22 ++++-- .../admin/organization/users/users.py | 44 +++++++++-- .../types/admin/organization/admin_api_key.py | 13 ++- .../admin_api_key_create_response.py | 2 +- src/openai/types/admin/organization/group.py | 3 + src/openai/types/admin/organization/invite.py | 14 ++-- .../admin/organization/organization_user.py | 79 +++++++++++++++++-- .../types/admin/organization/project.py | 15 ++-- .../organization/project_create_params.py | 8 +- .../organization/project_update_params.py | 11 ++- .../organization/projects/project_group.py | 3 + .../organization/projects/project_user.py | 15 ++-- .../projects/user_create_params.py | 10 ++- .../projects/user_update_params.py | 5 +- .../admin/organization/user_update_params.py | 14 +++- .../admin/organization/projects/test_users.py | 68 ++++++++++------ .../admin/organization/test_projects.py | 30 +++++-- .../admin/organization/test_users.py | 10 ++- 20 files changed, 317 insertions(+), 101 deletions(-) diff --git a/.stats.yml b/.stats.yml index c12fe18621..81cc0efb25 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-ea3c8bfb9cc34e798c5a473bb68ab5012344b1c99ab95377d0af4d908eb32a5d.yml -openapi_spec_hash: dbab3fdd7781b449ba3e9e1b5180c6ed +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-9a3e2bb9ea36990c39d7b633e6c10eb63d8918080560640a5b4cdccfac164761.yml +openapi_spec_hash: 0fa82e4dacd57a6d551e79d745860eb8 config_hash: 5d8a716125a61761563abbfc0d34e57c diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py index db774338d7..60d252cd46 100644 --- a/src/openai/resources/admin/organization/projects/projects.py +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Literal +from typing import Optional import httpx @@ -128,7 +128,8 @@ def create( self, *, name: str, - geography: Literal["US", "EU", "JP", "IN", "KR", "CA", "AU", "SG"] | Omit = omit, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -144,6 +145,8 @@ def create( Args: name: The friendly name of the project, this name appears in reports. + external_key_id: External key ID to associate with the project. + geography: Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) @@ -162,6 +165,7 @@ def create( body=maybe_transform( { "name": name, + "external_key_id": external_key_id, "geography": geography, }, project_create_params.ProjectCreateParams, @@ -217,7 +221,9 @@ def update( self, project_id: str, *, - name: str, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -229,6 +235,10 @@ def update( Modifies a project in the organization. Args: + external_key_id: External key ID to associate with the project. + + geography: Geography for the project. + name: The updated name of the project, this name appears in reports. extra_headers: Send extra headers @@ -243,7 +253,14 @@ def update( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._post( path_template("/organization/projects/{project_id}", project_id=project_id), - body=maybe_transform({"name": name}, project_update_params.ProjectUpdateParams), + body=maybe_transform( + { + "external_key_id": external_key_id, + "geography": geography, + "name": name, + }, + project_update_params.ProjectUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -404,7 +421,8 @@ async def create( self, *, name: str, - geography: Literal["US", "EU", "JP", "IN", "KR", "CA", "AU", "SG"] | Omit = omit, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -420,6 +438,8 @@ async def create( Args: name: The friendly name of the project, this name appears in reports. + external_key_id: External key ID to associate with the project. + geography: Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to use. See [data residency controls](https://platform.openai.com/docs/guides/your-data#data-residency-controls) @@ -438,6 +458,7 @@ async def create( body=await async_maybe_transform( { "name": name, + "external_key_id": external_key_id, "geography": geography, }, project_create_params.ProjectCreateParams, @@ -493,7 +514,9 @@ async def update( self, project_id: str, *, - name: str, + external_key_id: Optional[str] | Omit = omit, + geography: Optional[str] | Omit = omit, + name: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -505,6 +528,10 @@ async def update( Modifies a project in the organization. Args: + external_key_id: External key ID to associate with the project. + + geography: Geography for the project. + name: The updated name of the project, this name appears in reports. extra_headers: Send extra headers @@ -519,7 +546,14 @@ async def update( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return await self._post( path_template("/organization/projects/{project_id}", project_id=project_id), - body=await async_maybe_transform({"name": name}, project_update_params.ProjectUpdateParams), + body=await async_maybe_transform( + { + "external_key_id": external_key_id, + "geography": geography, + "name": name, + }, + project_update_params.ProjectUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/projects/users/users.py b/src/openai/resources/admin/organization/projects/users/users.py index 024fc27043..3852bfb8c0 100644 --- a/src/openai/resources/admin/organization/projects/users/users.py +++ b/src/openai/resources/admin/organization/projects/users/users.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Literal +from typing import Optional import httpx @@ -57,8 +57,9 @@ def create( self, project_id: str, *, - role: Literal["owner", "member"], - user_id: str, + role: str, + email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -74,6 +75,8 @@ def create( Args: role: `owner` or `member` + email: Email of the user to add. + user_id: The ID of the user. extra_headers: Send extra headers @@ -91,6 +94,7 @@ def create( body=maybe_transform( { "role": role, + "email": email, "user_id": user_id, }, user_create_params.UserCreateParams, @@ -152,7 +156,7 @@ def update( user_id: str, *, project_id: str, - role: Literal["owner", "member"], + role: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -322,8 +326,9 @@ async def create( self, project_id: str, *, - role: Literal["owner", "member"], - user_id: str, + role: str, + email: Optional[str] | Omit = omit, + user_id: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -339,6 +344,8 @@ async def create( Args: role: `owner` or `member` + email: Email of the user to add. + user_id: The ID of the user. extra_headers: Send extra headers @@ -356,6 +363,7 @@ async def create( body=await async_maybe_transform( { "role": role, + "email": email, "user_id": user_id, }, user_create_params.UserCreateParams, @@ -417,7 +425,7 @@ async def update( user_id: str, *, project_id: str, - role: Literal["owner", "member"], + role: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, diff --git a/src/openai/resources/admin/organization/users/users.py b/src/openai/resources/admin/organization/users/users.py index b65a138d1a..94eab69e83 100644 --- a/src/openai/resources/admin/organization/users/users.py +++ b/src/openai/resources/admin/organization/users/users.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Literal +from typing import Optional import httpx @@ -94,7 +94,10 @@ def update( self, user_id: str, *, - role: Literal["owner", "reader"] | Omit = omit, + developer_persona: Optional[str] | Omit = omit, + role: Optional[str] | Omit = omit, + role_id: Optional[str] | Omit = omit, + technical_level: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -106,8 +109,14 @@ def update( Modifies a user's role in the organization. Args: + developer_persona: Developer persona metadata. + role: `owner` or `reader` + role_id: Role ID to assign to the user. + + technical_level: Technical level metadata. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -120,7 +129,15 @@ def update( raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") return self._post( path_template("/organization/users/{user_id}", user_id=user_id), - body=maybe_transform({"role": role}, user_update_params.UserUpdateParams), + body=maybe_transform( + { + "developer_persona": developer_persona, + "role": role, + "role_id": role_id, + "technical_level": technical_level, + }, + user_update_params.UserUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -290,7 +307,10 @@ async def update( self, user_id: str, *, - role: Literal["owner", "reader"] | Omit = omit, + developer_persona: Optional[str] | Omit = omit, + role: Optional[str] | Omit = omit, + role_id: Optional[str] | Omit = omit, + technical_level: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -302,8 +322,14 @@ async def update( Modifies a user's role in the organization. Args: + developer_persona: Developer persona metadata. + role: `owner` or `reader` + role_id: Role ID to assign to the user. + + technical_level: Technical level metadata. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -316,7 +342,15 @@ async def update( raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") return await self._post( path_template("/organization/users/{user_id}", user_id=user_id), - body=await async_maybe_transform({"role": role}, user_update_params.UserUpdateParams), + body=await async_maybe_transform( + { + "developer_persona": developer_persona, + "role": role, + "role_id": role_id, + "technical_level": technical_level, + }, + user_update_params.UserUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/types/admin/organization/admin_api_key.py b/src/openai/types/admin/organization/admin_api_key.py index 76951a99dd..5d2e5840f9 100644 --- a/src/openai/types/admin/organization/admin_api_key.py +++ b/src/openai/types/admin/organization/admin_api_key.py @@ -37,12 +37,6 @@ class AdminAPIKey(BaseModel): created_at: int """The Unix timestamp (in seconds) of when the API key was created""" - last_used_at: Optional[int] = None - """The Unix timestamp (in seconds) of when the API key was last used""" - - name: str - """The name of the API key""" - object: Literal["organization.admin_api_key"] """The object type, which is always `organization.admin_api_key`""" @@ -51,5 +45,8 @@ class AdminAPIKey(BaseModel): redacted_value: str """The redacted value of the API key""" - value: Optional[str] = None - """The value of the API key. Only shown on create.""" + last_used_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the API key was last used""" + + name: Optional[str] = None + """The name of the API key""" diff --git a/src/openai/types/admin/organization/admin_api_key_create_response.py b/src/openai/types/admin/organization/admin_api_key_create_response.py index 48b2aaaaba..58101a9e0a 100644 --- a/src/openai/types/admin/organization/admin_api_key_create_response.py +++ b/src/openai/types/admin/organization/admin_api_key_create_response.py @@ -8,5 +8,5 @@ class AdminAPIKeyCreateResponse(AdminAPIKey): """Represents an individual Admin API key in an org.""" - value: str # type: ignore + value: str """The value of the API key. Only shown on create.""" diff --git a/src/openai/types/admin/organization/group.py b/src/openai/types/admin/organization/group.py index ce3b0d41b3..a5823b1442 100644 --- a/src/openai/types/admin/organization/group.py +++ b/src/openai/types/admin/organization/group.py @@ -14,6 +14,9 @@ class Group(BaseModel): created_at: int """Unix timestamp (in seconds) when the group was created.""" + group_type: str + """The type of the group.""" + is_scim_managed: bool """ Whether the group is managed through SCIM and controlled by your identity diff --git a/src/openai/types/admin/organization/invite.py b/src/openai/types/admin/organization/invite.py index dc72dc2b5b..a3d2c50438 100644 --- a/src/openai/types/admin/organization/invite.py +++ b/src/openai/types/admin/organization/invite.py @@ -9,10 +9,10 @@ class Project(BaseModel): - id: Optional[str] = None + id: str """Project's public ID""" - role: Optional[Literal["member", "owner"]] = None + role: Literal["member", "owner"] """Project membership role""" @@ -28,12 +28,12 @@ class Invite(BaseModel): email: str """The email address of the individual to whom the invite was sent""" - expires_at: Optional[int] = None - """The Unix timestamp (in seconds) of when the invite expires.""" - object: Literal["organization.invite"] """The object type, which is always `organization.invite`""" + projects: List[Project] + """The projects that were granted membership upon acceptance of the invite.""" + role: Literal["owner", "reader"] """`owner` or `reader`""" @@ -43,5 +43,5 @@ class Invite(BaseModel): accepted_at: Optional[int] = None """The Unix timestamp (in seconds) of when the invite was accepted.""" - projects: Optional[List[Project]] = None - """The projects that were granted membership upon acceptance of the invite.""" + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the invite expires.""" diff --git a/src/openai/types/admin/organization/organization_user.py b/src/openai/types/admin/organization/organization_user.py index 3ffe572465..3d1d43a8b6 100644 --- a/src/openai/types/admin/organization/organization_user.py +++ b/src/openai/types/admin/organization/organization_user.py @@ -1,10 +1,47 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Optional from typing_extensions import Literal from ...._models import BaseModel -__all__ = ["OrganizationUser"] +__all__ = ["OrganizationUser", "Projects", "ProjectsData", "User"] + + +class ProjectsData(BaseModel): + id: Optional[str] = None + + name: Optional[str] = None + + role: Optional[str] = None + + +class Projects(BaseModel): + """Projects associated with the user, if included.""" + + data: List[ProjectsData] + + object: Literal["list"] + + +class User(BaseModel): + """Nested user details.""" + + id: str + + object: Literal["user"] + + banned: Optional[bool] = None + + banned_at: Optional[int] = None + + email: Optional[str] = None + + enabled: Optional[bool] = None + + name: Optional[str] = None + + picture: Optional[str] = None class OrganizationUser(BaseModel): @@ -16,14 +53,44 @@ class OrganizationUser(BaseModel): added_at: int """The Unix timestamp (in seconds) of when the user was added.""" - email: str + object: Literal["organization.user"] + """The object type, which is always `organization.user`""" + + api_key_last_used_at: Optional[int] = None + """The Unix timestamp (in seconds) of the user's last API key usage.""" + + created: Optional[int] = None + """The Unix timestamp (in seconds) of when the user was created.""" + + developer_persona: Optional[str] = None + """The developer persona metadata for the user.""" + + email: Optional[str] = None """The email address of the user""" - name: str + is_default: Optional[bool] = None + """Whether this is the organization's default user.""" + + is_scale_tier_authorized_purchaser: Optional[bool] = None + """Whether the user is an authorized purchaser for Scale Tier.""" + + is_scim_managed: Optional[bool] = None + """Whether the user is managed through SCIM.""" + + is_service_account: Optional[bool] = None + """Whether the user is a service account.""" + + name: Optional[str] = None """The name of the user""" - object: Literal["organization.user"] - """The object type, which is always `organization.user`""" + projects: Optional[Projects] = None + """Projects associated with the user, if included.""" - role: Literal["owner", "reader"] + role: Optional[str] = None """`owner` or `reader`""" + + technical_level: Optional[str] = None + """The technical level metadata for the user.""" + + user: Optional[User] = None + """Nested user details.""" diff --git a/src/openai/types/admin/organization/project.py b/src/openai/types/admin/organization/project.py index a1058af3d5..982bb1e4b3 100644 --- a/src/openai/types/admin/organization/project.py +++ b/src/openai/types/admin/organization/project.py @@ -17,14 +17,17 @@ class Project(BaseModel): created_at: int """The Unix timestamp (in seconds) of when the project was created.""" - name: str - """The name of the project. This appears in reporting.""" - object: Literal["organization.project"] """The object type, which is always `organization.project`""" - status: Literal["active", "archived"] - """`active` or `archived`""" - archived_at: Optional[int] = None """The Unix timestamp (in seconds) of when the project was archived or `null`.""" + + external_key_id: Optional[str] = None + """The external key associated with the project.""" + + name: Optional[str] = None + """The name of the project. This appears in reporting.""" + + status: Optional[str] = None + """`active` or `archived`""" diff --git a/src/openai/types/admin/organization/project_create_params.py b/src/openai/types/admin/organization/project_create_params.py index 8b8d1b44e8..a4b7b2d424 100644 --- a/src/openai/types/admin/organization/project_create_params.py +++ b/src/openai/types/admin/organization/project_create_params.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Optional +from typing_extensions import Required, TypedDict __all__ = ["ProjectCreateParams"] @@ -11,7 +12,10 @@ class ProjectCreateParams(TypedDict, total=False): name: Required[str] """The friendly name of the project, this name appears in reports.""" - geography: Literal["US", "EU", "JP", "IN", "KR", "CA", "AU", "SG"] + external_key_id: Optional[str] + """External key ID to associate with the project.""" + + geography: Optional[str] """Create the project with the specified data residency region. Your organization must have access to Data residency functionality in order to diff --git a/src/openai/types/admin/organization/project_update_params.py b/src/openai/types/admin/organization/project_update_params.py index 0ba1984a9f..2ebdd09f4a 100644 --- a/src/openai/types/admin/organization/project_update_params.py +++ b/src/openai/types/admin/organization/project_update_params.py @@ -2,11 +2,18 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing import Optional +from typing_extensions import TypedDict __all__ = ["ProjectUpdateParams"] class ProjectUpdateParams(TypedDict, total=False): - name: Required[str] + external_key_id: Optional[str] + """External key ID to associate with the project.""" + + geography: Optional[str] + """Geography for the project.""" + + name: Optional[str] """The updated name of the project, this name appears in reports.""" diff --git a/src/openai/types/admin/organization/projects/project_group.py b/src/openai/types/admin/organization/projects/project_group.py index e80d815795..b3da08ca32 100644 --- a/src/openai/types/admin/organization/projects/project_group.py +++ b/src/openai/types/admin/organization/projects/project_group.py @@ -19,6 +19,9 @@ class ProjectGroup(BaseModel): group_name: str """Display name of the group.""" + group_type: str + """The type of the group.""" + object: Literal["project.group"] """Always `project.group`.""" diff --git a/src/openai/types/admin/organization/projects/project_user.py b/src/openai/types/admin/organization/projects/project_user.py index 68c73d1c9b..7aaa9fc05b 100644 --- a/src/openai/types/admin/organization/projects/project_user.py +++ b/src/openai/types/admin/organization/projects/project_user.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ....._models import BaseModel @@ -16,14 +17,14 @@ class ProjectUser(BaseModel): added_at: int """The Unix timestamp (in seconds) of when the project was added.""" - email: str - """The email address of the user""" - - name: str - """The name of the user""" - object: Literal["organization.project.user"] """The object type, which is always `organization.project.user`""" - role: Literal["owner", "member"] + role: str """`owner` or `member`""" + + email: Optional[str] = None + """The email address of the user""" + + name: Optional[str] = None + """The name of the user""" diff --git a/src/openai/types/admin/organization/projects/user_create_params.py b/src/openai/types/admin/organization/projects/user_create_params.py index dee9d126db..4266ed01f9 100644 --- a/src/openai/types/admin/organization/projects/user_create_params.py +++ b/src/openai/types/admin/organization/projects/user_create_params.py @@ -2,14 +2,18 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Optional +from typing_extensions import Required, TypedDict __all__ = ["UserCreateParams"] class UserCreateParams(TypedDict, total=False): - role: Required[Literal["owner", "member"]] + role: Required[str] """`owner` or `member`""" - user_id: Required[str] + email: Optional[str] + """Email of the user to add.""" + + user_id: Optional[str] """The ID of the user.""" diff --git a/src/openai/types/admin/organization/projects/user_update_params.py b/src/openai/types/admin/organization/projects/user_update_params.py index 08b3e1a4e9..20a4276567 100644 --- a/src/openai/types/admin/organization/projects/user_update_params.py +++ b/src/openai/types/admin/organization/projects/user_update_params.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Optional +from typing_extensions import Required, TypedDict __all__ = ["UserUpdateParams"] @@ -10,5 +11,5 @@ class UserUpdateParams(TypedDict, total=False): project_id: Required[str] - role: Required[Literal["owner", "member"]] + role: Optional[str] """`owner` or `member`""" diff --git a/src/openai/types/admin/organization/user_update_params.py b/src/openai/types/admin/organization/user_update_params.py index ab1f9d0026..24181b0649 100644 --- a/src/openai/types/admin/organization/user_update_params.py +++ b/src/openai/types/admin/organization/user_update_params.py @@ -2,11 +2,21 @@ from __future__ import annotations -from typing_extensions import Literal, TypedDict +from typing import Optional +from typing_extensions import TypedDict __all__ = ["UserUpdateParams"] class UserUpdateParams(TypedDict, total=False): - role: Literal["owner", "reader"] + developer_persona: Optional[str] + """Developer persona metadata.""" + + role: Optional[str] """`owner` or `reader`""" + + role_id: Optional[str] + """Role ID to assign to the user.""" + + technical_level: Optional[str] + """Technical level metadata.""" diff --git a/tests/api_resources/admin/organization/projects/test_users.py b/tests/api_resources/admin/organization/projects/test_users.py index 30ad94fe3b..66005bf657 100644 --- a/tests/api_resources/admin/organization/projects/test_users.py +++ b/tests/api_resources/admin/organization/projects/test_users.py @@ -25,7 +25,16 @@ class TestUsers: def test_method_create(self, client: OpenAI) -> None: user = client.admin.organization.projects.users.create( project_id="project_id", - role="owner", + role="role", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.create( + project_id="project_id", + role="role", + email="email", user_id="user_id", ) assert_matches_type(ProjectUser, user, path=["response"]) @@ -34,8 +43,7 @@ def test_method_create(self, client: OpenAI) -> None: def test_raw_response_create(self, client: OpenAI) -> None: response = client.admin.organization.projects.users.with_raw_response.create( project_id="project_id", - role="owner", - user_id="user_id", + role="role", ) assert response.is_closed is True @@ -47,8 +55,7 @@ def test_raw_response_create(self, client: OpenAI) -> None: def test_streaming_response_create(self, client: OpenAI) -> None: with client.admin.organization.projects.users.with_streaming_response.create( project_id="project_id", - role="owner", - user_id="user_id", + role="role", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -63,8 +70,7 @@ def test_path_params_create(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): client.admin.organization.projects.users.with_raw_response.create( project_id="", - role="owner", - user_id="user_id", + role="role", ) @parametrize @@ -120,7 +126,15 @@ def test_method_update(self, client: OpenAI) -> None: user = client.admin.organization.projects.users.update( user_id="user_id", project_id="project_id", - role="owner", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + user = client.admin.organization.projects.users.update( + user_id="user_id", + project_id="project_id", + role="role", ) assert_matches_type(ProjectUser, user, path=["response"]) @@ -129,7 +143,6 @@ def test_raw_response_update(self, client: OpenAI) -> None: response = client.admin.organization.projects.users.with_raw_response.update( user_id="user_id", project_id="project_id", - role="owner", ) assert response.is_closed is True @@ -142,7 +155,6 @@ def test_streaming_response_update(self, client: OpenAI) -> None: with client.admin.organization.projects.users.with_streaming_response.update( user_id="user_id", project_id="project_id", - role="owner", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -158,14 +170,12 @@ def test_path_params_update(self, client: OpenAI) -> None: client.admin.organization.projects.users.with_raw_response.update( user_id="user_id", project_id="", - role="owner", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): client.admin.organization.projects.users.with_raw_response.update( user_id="", project_id="project_id", - role="owner", ) @parametrize @@ -273,7 +283,16 @@ class TestAsyncUsers: async def test_method_create(self, async_client: AsyncOpenAI) -> None: user = await async_client.admin.organization.projects.users.create( project_id="project_id", - role="owner", + role="role", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.create( + project_id="project_id", + role="role", + email="email", user_id="user_id", ) assert_matches_type(ProjectUser, user, path=["response"]) @@ -282,8 +301,7 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.projects.users.with_raw_response.create( project_id="project_id", - role="owner", - user_id="user_id", + role="role", ) assert response.is_closed is True @@ -295,8 +313,7 @@ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: async with async_client.admin.organization.projects.users.with_streaming_response.create( project_id="project_id", - role="owner", - user_id="user_id", + role="role", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -311,8 +328,7 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): await async_client.admin.organization.projects.users.with_raw_response.create( project_id="", - role="owner", - user_id="user_id", + role="role", ) @parametrize @@ -368,7 +384,15 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: user = await async_client.admin.organization.projects.users.update( user_id="user_id", project_id="project_id", - role="owner", + ) + assert_matches_type(ProjectUser, user, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.projects.users.update( + user_id="user_id", + project_id="project_id", + role="role", ) assert_matches_type(ProjectUser, user, path=["response"]) @@ -377,7 +401,6 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.projects.users.with_raw_response.update( user_id="user_id", project_id="project_id", - role="owner", ) assert response.is_closed is True @@ -390,7 +413,6 @@ async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> Non async with async_client.admin.organization.projects.users.with_streaming_response.update( user_id="user_id", project_id="project_id", - role="owner", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -406,14 +428,12 @@ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: await async_client.admin.organization.projects.users.with_raw_response.update( user_id="user_id", project_id="", - role="owner", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): await async_client.admin.organization.projects.users.with_raw_response.update( user_id="", project_id="project_id", - role="owner", ) @parametrize diff --git a/tests/api_resources/admin/organization/test_projects.py b/tests/api_resources/admin/organization/test_projects.py index 49c18827de..5e07ff1496 100644 --- a/tests/api_resources/admin/organization/test_projects.py +++ b/tests/api_resources/admin/organization/test_projects.py @@ -29,7 +29,8 @@ def test_method_create(self, client: OpenAI) -> None: def test_method_create_with_all_params(self, client: OpenAI) -> None: project = client.admin.organization.projects.create( name="name", - geography="US", + external_key_id="external_key_id", + geography="geography", ) assert_matches_type(Project, project, path=["response"]) @@ -99,6 +100,15 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: def test_method_update(self, client: OpenAI) -> None: project = client.admin.organization.projects.update( project_id="project_id", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + project = client.admin.organization.projects.update( + project_id="project_id", + external_key_id="external_key_id", + geography="geography", name="name", ) assert_matches_type(Project, project, path=["response"]) @@ -107,7 +117,6 @@ def test_method_update(self, client: OpenAI) -> None: def test_raw_response_update(self, client: OpenAI) -> None: response = client.admin.organization.projects.with_raw_response.update( project_id="project_id", - name="name", ) assert response.is_closed is True @@ -119,7 +128,6 @@ def test_raw_response_update(self, client: OpenAI) -> None: def test_streaming_response_update(self, client: OpenAI) -> None: with client.admin.organization.projects.with_streaming_response.update( project_id="project_id", - name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -134,7 +142,6 @@ def test_path_params_update(self, client: OpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): client.admin.organization.projects.with_raw_response.update( project_id="", - name="name", ) @parametrize @@ -226,7 +233,8 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: project = await async_client.admin.organization.projects.create( name="name", - geography="US", + external_key_id="external_key_id", + geography="geography", ) assert_matches_type(Project, project, path=["response"]) @@ -296,6 +304,15 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: async def test_method_update(self, async_client: AsyncOpenAI) -> None: project = await async_client.admin.organization.projects.update( project_id="project_id", + ) + assert_matches_type(Project, project, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + project = await async_client.admin.organization.projects.update( + project_id="project_id", + external_key_id="external_key_id", + geography="geography", name="name", ) assert_matches_type(Project, project, path=["response"]) @@ -304,7 +321,6 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.projects.with_raw_response.update( project_id="project_id", - name="name", ) assert response.is_closed is True @@ -316,7 +332,6 @@ async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: async with async_client.admin.organization.projects.with_streaming_response.update( project_id="project_id", - name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -331,7 +346,6 @@ async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): await async_client.admin.organization.projects.with_raw_response.update( project_id="", - name="name", ) @parametrize diff --git a/tests/api_resources/admin/organization/test_users.py b/tests/api_resources/admin/organization/test_users.py index 8107bc5e53..308b199bc6 100644 --- a/tests/api_resources/admin/organization/test_users.py +++ b/tests/api_resources/admin/organization/test_users.py @@ -67,7 +67,10 @@ def test_method_update(self, client: OpenAI) -> None: def test_method_update_with_all_params(self, client: OpenAI) -> None: user = client.admin.organization.users.update( user_id="user_id", - role="owner", + developer_persona="developer_persona", + role="role", + role_id="role_id", + technical_level="technical_level", ) assert_matches_type(OrganizationUser, user, path=["response"]) @@ -229,7 +232,10 @@ async def test_method_update(self, async_client: AsyncOpenAI) -> None: async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: user = await async_client.admin.organization.users.update( user_id="user_id", - role="owner", + developer_persona="developer_persona", + role="role", + role_id="role_id", + technical_level="technical_level", ) assert_matches_type(OrganizationUser, user, path=["response"]) From 8428b0f2cf64f3d9a8ce188823e034ee35eac110 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 21:39:01 +0000 Subject: [PATCH 332/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 81cc0efb25..151eefd946 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-9a3e2bb9ea36990c39d7b633e6c10eb63d8918080560640a5b4cdccfac164761.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-183d8a06e082c4d50be9b29533adf1f0806646ee0a9582fc51386d629b1e9ffe.yml openapi_spec_hash: 0fa82e4dacd57a6d551e79d745860eb8 -config_hash: 5d8a716125a61761563abbfc0d34e57c +config_hash: dd484e2cc01206d26516338d0f4596b0 From 529e3b746c5c1bf562eba6e37f8a30eab2a0106b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 23:04:37 +0000 Subject: [PATCH 333/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 151eefd946..723cea85fe 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-183d8a06e082c4d50be9b29533adf1f0806646ee0a9582fc51386d629b1e9ffe.yml -openapi_spec_hash: 0fa82e4dacd57a6d551e79d745860eb8 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-21ecab7aeb61612b9da5e52ea4c0cb75a33d443d975022934b9305e97d1a7d62.yml +openapi_spec_hash: cfc868a0bb3567183510c9b5629c510f config_hash: dd484e2cc01206d26516338d0f4596b0 From 9370977d155464e499de2ac05ee3e99fcd694fa3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 14:25:08 +0000 Subject: [PATCH 334/408] release: 2.34.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 46 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7ef8288ed5..7a1c4674e4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.33.0" + ".": "2.34.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index effb1cc263..237a8c00a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,51 @@ # Changelog +## 2.34.0 (2026-05-04) + +Full Changelog: [v2.33.0...v2.34.0](https://github.com/openai/openai-python/compare/v2.33.0...v2.34.0) + +### Features + +* **api:** add external_key_id to projects, email/metadata params to users, update types ([2d232ee](https://github.com/openai/openai-python/commit/2d232eebb2fe021bb21f2576b17d1d588f81a608)) +* **api:** add support for Admin API Keys per endpoint ([b8b176a](https://github.com/openai/openai-python/commit/b8b176af84172f27d2fde8dca062ca4c41f94bf7)) +* **api:** admin API updates ([4ae1138](https://github.com/openai/openai-python/commit/4ae1138ae1f76e81a2267e4deb45b435c10774d5)) +* **api:** manual updates ([c1870f1](https://github.com/openai/openai-python/commit/c1870f1b881bb914e4e62a6c8b08d4c2b9a6fd54)) +* **api:** manual updates ([f6bb9c7](https://github.com/openai/openai-python/commit/f6bb9c7d7bdcc45425d37722358bed097e83d493)) +* support setting headers via env ([1e89d8b](https://github.com/openai/openai-python/commit/1e89d8b56aba12f99a8ef2b1b78fdee84751275a)) + + +### Bug Fixes + +* allow explicit Azure auth headers ([a0626ba](https://github.com/openai/openai-python/commit/a0626babf0548fb03cf3c2d054da116dd6466701)) +* **api:** correct prompt_cache_retention enum value from in-memory to in_memory ([d47d9f0](https://github.com/openai/openai-python/commit/d47d9f0f79c612c4d14005a0a3cf44e1968c9bff)) +* **api:** preserve python api key attribute type ([62607f6](https://github.com/openai/openai-python/commit/62607f61c542ed559ef114849e31307c0c290286)) +* **api:** resolve python auth type checks ([42a31a7](https://github.com/openai/openai-python/commit/42a31a7efb6784633108c1a73e1779ed79ab8bed)) +* **api:** support admin api key auth ([f029eb9](https://github.com/openai/openai-python/commit/f029eb937f976110c1a67b9342525a38a214072e)) +* avoid bearer fallback for admin auth ([22e01a8](https://github.com/openai/openai-python/commit/22e01a8cf791a143ecc576f46de50eee9b3c2147)) +* preserve selected auth credentials ([0d27f9d](https://github.com/openai/openai-python/commit/0d27f9dbd3b2ae82b2e8c2eeb9e7e78f3edecdf1)) +* require bearer auth for stream helpers ([d055539](https://github.com/openai/openai-python/commit/d0555390bcf4a704c10d318c7de2fe006750c3d0)) +* **types:** correct created_at and completed_at to float in Response ([7da4b88](https://github.com/openai/openai-python/commit/7da4b88c1985028f7ee9a98b919e71f863f979f0)) +* **types:** correct timestamp types to int in Response model ([e55631c](https://github.com/openai/openai-python/commit/e55631c868b1d0b720fda0abdbc342787cd95e2c)) +* use correct field name format for multipart file arrays ([9ee4825](https://github.com/openai/openai-python/commit/9ee482576c2bd6b33b6cf7458c37ab2e7d5bc725)) + + +### Performance Improvements + +* **client:** optimize file structure copying in multipart requests ([dca474e](https://github.com/openai/openai-python/commit/dca474e5beac7cc8e05855f042c3227843030c1b)) + + +### Chores + +* **internal:** more robust bootstrap script ([9ec1600](https://github.com/openai/openai-python/commit/9ec1600d48fda10abb144b2a62d07c5abd7e9ab1)) +* **internal:** reformat pyproject.toml ([12ad57b](https://github.com/openai/openai-python/commit/12ad57b8da5b5c0615641af273d4bbf2981d6bf7)) +* **tests:** bump steady to v0.22.1 ([486dfed](https://github.com/openai/openai-python/commit/486dfedfec8484bb00318b0ea798c2260f7a720c)) + + +### Documentation + +* **api:** add rate limit and vector store info to files create ([4f776df](https://github.com/openai/openai-python/commit/4f776df78d757fdbf25662c4be98b5c98183aaaf)) +* **api:** update files rate limit documentation ([b141a20](https://github.com/openai/openai-python/commit/b141a20e948b5af3b8fbe4261798c191d2857b4a)) + ## 2.33.0 (2026-04-28) Full Changelog: [v2.32.0...v2.33.0](https://github.com/openai/openai-python/compare/v2.32.0...v2.33.0) diff --git a/pyproject.toml b/pyproject.toml index b2f4dd11cb..7fbf3bd49b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.33.0" +version = "2.34.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b73f7aa7bd..857aeb7dff 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.33.0" # x-release-please-version +__version__ = "2.34.0" # x-release-please-version From a229b37a1af8b4efe5e59672be7b473d1bb44edd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 19:09:50 +0000 Subject: [PATCH 335/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 723cea85fe..ff1ede1c54 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-21ecab7aeb61612b9da5e52ea4c0cb75a33d443d975022934b9305e97d1a7d62.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-81b872fc8a50941a72f77f3fa0791f7234b9180db95b451b38ced94279e947c5.yml openapi_spec_hash: cfc868a0bb3567183510c9b5629c510f -config_hash: dd484e2cc01206d26516338d0f4596b0 +config_hash: 4a32815d42629ed593422278834dcca4 From 6069e19a816e12c076fda6f1207094fdfb4692fe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 20:44:50 +0000 Subject: [PATCH 336/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ff1ede1c54..3e1ed4b9e5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-81b872fc8a50941a72f77f3fa0791f7234b9180db95b451b38ced94279e947c5.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-b29ed93b301b4a89daf4fb5bb6463cbc059ce944ff9808639679794c10e60dd6.yml openapi_spec_hash: cfc868a0bb3567183510c9b5629c510f -config_hash: 4a32815d42629ed593422278834dcca4 +config_hash: 2524657a4d3e2779f4a70cc581c33d80 From 6dd268a36ae6c4dd73cfcf2086e5a8c49ba7c116 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 21:31:50 +0000 Subject: [PATCH 337/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3e1ed4b9e5..1c61ea53ec 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-b29ed93b301b4a89daf4fb5bb6463cbc059ce944ff9808639679794c10e60dd6.yml -openapi_spec_hash: cfc868a0bb3567183510c9b5629c510f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-70926f399403fb20d9cee460500dad50a9555dd72b5abb768215e9f541a648ea.yml +openapi_spec_hash: ae4a7852e20f39c14adbf6f253b8a004 config_hash: 2524657a4d3e2779f4a70cc581c33d80 From 641c8c487f1e0e786bac17c55f5d8536c5e0336d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 00:42:06 +0000 Subject: [PATCH 338/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1c61ea53ec..0b6e9119d8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-70926f399403fb20d9cee460500dad50a9555dd72b5abb768215e9f541a648ea.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-61d75f384a1e6bee387e9e02168257426facdbbb9677b9b5cb30a3863da40b10.yml openapi_spec_hash: ae4a7852e20f39c14adbf6f253b8a004 -config_hash: 2524657a4d3e2779f4a70cc581c33d80 +config_hash: 51d639d7939b6ab974d1ee45e96c532b From 408dce57fe849024b6a4e9e45797b62c8dc6d97f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 08:07:54 +0000 Subject: [PATCH 339/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0b6e9119d8..85f4cd414b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-61d75f384a1e6bee387e9e02168257426facdbbb9677b9b5cb30a3863da40b10.yml -openapi_spec_hash: ae4a7852e20f39c14adbf6f253b8a004 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-1405c20d24646f71eecd0f7ed222426973b8806ef70a3865a2428c0c57b1f050.yml +openapi_spec_hash: 92713b0825f6b8760836778e6d27e837 config_hash: 51d639d7939b6ab974d1ee45e96c532b From 0ba55d7569565045426e1587906a70d5682a4bba Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 15:49:42 +0000 Subject: [PATCH 340/408] feat(api): launch realtime translate + update image 2 --- .stats.yml | 4 +- src/openai/resources/images.py | 108 +++++++++++------- src/openai/types/image_edit_params.py | 15 ++- src/openai/types/image_generate_params.py | 5 +- src/openai/types/image_model.py | 11 +- .../types/realtime/translations/__init__.py | 3 + src/openai/types/responses/tool.py | 13 ++- src/openai/types/responses/tool_param.py | 12 +- tests/api_resources/test_images.py | 20 ++-- 9 files changed, 127 insertions(+), 64 deletions(-) create mode 100644 src/openai/types/realtime/translations/__init__.py diff --git a/.stats.yml b/.stats.yml index 85f4cd414b..1dee1de615 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-1405c20d24646f71eecd0f7ed222426973b8806ef70a3865a2428c0c57b1f050.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5ae2eda70e6a4a375842fb8bf3e88deab8e0aad1b3871b86e73274f131bb37ce.yml openapi_spec_hash: 92713b0825f6b8760836778e6d27e837 -config_hash: 51d639d7939b6ab974d1ee45e96c532b +config_hash: c15a744b2771a3948032b2438475b330 diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index ec4ca23aa2..1b3ade12c9 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -160,10 +160,10 @@ def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and - `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same - input constraints as GPT image models. + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image + should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to + 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -189,7 +189,10 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Defaults to `gpt-image-1.5`. + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -273,10 +276,10 @@ def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and - `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same - input constraints as GPT image models. + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image + should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to + 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -306,7 +309,10 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Defaults to `gpt-image-1.5`. + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -386,10 +392,10 @@ def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and - `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same - input constraints as GPT image models. + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image + should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to + 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -419,7 +425,10 @@ def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Defaults to `gpt-image-1.5`. + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -580,8 +589,9 @@ def generate( be set to either `png` (default value) or `webp`. model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT - image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to - `dall-e-2` unless a parameter specific to the GPT image models is used. + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific + to the GPT image models is used. moderation: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default @@ -695,8 +705,9 @@ def generate( be set to either `png` (default value) or `webp`. model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT - image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to - `dall-e-2` unless a parameter specific to the GPT image models is used. + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific + to the GPT image models is used. moderation: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default @@ -806,8 +817,9 @@ def generate( be set to either `png` (default value) or `webp`. model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT - image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to - `dall-e-2` unless a parameter specific to the GPT image models is used. + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific + to the GPT image models is used. moderation: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default @@ -1066,10 +1078,10 @@ async def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and - `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same - input constraints as GPT image models. + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image + should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to + 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -1095,7 +1107,10 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Defaults to `gpt-image-1.5`. + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -1179,10 +1194,10 @@ async def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and - `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same - input constraints as GPT image models. + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image + should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to + 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -1212,7 +1227,10 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Defaults to `gpt-image-1.5`. + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -1292,10 +1310,10 @@ async def edit( Args: image: The image(s) to edit. Must be a supported image file or an array of images. - For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and - `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same - input constraints as GPT image models. + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image + should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to + 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -1325,7 +1343,10 @@ async def edit( the mask will be applied on the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as `image`. - model: The model to use for image generation. Defaults to `gpt-image-1.5`. + model: The model to use for image generation. One of `dall-e-2` or a GPT image model + (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + `gpt-image-2-2026-04-21`, or `chatgpt-image-latest`). Defaults to + `gpt-image-1.5`. n: The number of images to generate. Must be between 1 and 10. @@ -1486,8 +1507,9 @@ async def generate( be set to either `png` (default value) or `webp`. model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT - image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to - `dall-e-2` unless a parameter specific to the GPT image models is used. + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific + to the GPT image models is used. moderation: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default @@ -1601,8 +1623,9 @@ async def generate( be set to either `png` (default value) or `webp`. model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT - image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to - `dall-e-2` unless a parameter specific to the GPT image models is used. + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific + to the GPT image models is used. moderation: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default @@ -1712,8 +1735,9 @@ async def generate( be set to either `png` (default value) or `webp`. model: The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT - image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to - `dall-e-2` unless a parameter specific to the GPT image models is used. + image model (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, + or `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific + to the GPT image models is used. moderation: Control the content-moderation level for images generated by the GPT image models. Must be either `low` for less restrictive filtering or `auto` (default diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 05f3401d2d..ab70789fe6 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -15,10 +15,10 @@ class ImageEditParamsBase(TypedDict, total=False): image: Required[Union[FileTypes, SequenceNotStr[FileTypes]]] """The image(s) to edit. Must be a supported image file or an array of images. - For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, and - `gpt-image-1.5`), each image should be a `png`, `webp`, or `jpg` file less than - 50MB. You can provide up to 16 images. `chatgpt-image-latest` follows the same - input constraints as GPT image models. + For the GPT image models (`gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, + `gpt-image-2`, `gpt-image-2-2026-04-21`, and `chatgpt-image-latest`), each image + should be a `png`, `webp`, or `jpg` file less than 50MB. You can provide up to + 16 images. For `dall-e-2`, you can only provide one image, and it should be a square `png` file less than 4MB. @@ -59,7 +59,12 @@ class ImageEditParamsBase(TypedDict, total=False): """ model: Union[str, ImageModel, None] - """The model to use for image generation. Defaults to `gpt-image-1.5`.""" + """The model to use for image generation. + + One of `dall-e-2` or a GPT image model (`gpt-image-1`, `gpt-image-1-mini`, + `gpt-image-1.5`, `gpt-image-2`, `gpt-image-2-2026-04-21`, or + `chatgpt-image-latest`). Defaults to `gpt-image-1.5`. + """ n: Optional[int] """The number of images to generate. Must be between 1 and 10.""" diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 7a95b3dd3d..30a514cffb 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -33,8 +33,9 @@ class ImageGenerateParamsBase(TypedDict, total=False): """The model to use for image generation. One of `dall-e-2`, `dall-e-3`, or a GPT image model (`gpt-image-1`, - `gpt-image-1-mini`, `gpt-image-1.5`). Defaults to `dall-e-2` unless a parameter - specific to the GPT image models is used. + `gpt-image-1-mini`, `gpt-image-1.5`, `gpt-image-2`, or + `gpt-image-2-2026-04-21`). Defaults to `dall-e-2` unless a parameter specific to + the GPT image models is used. """ moderation: Optional[Literal["low", "auto"]] diff --git a/src/openai/types/image_model.py b/src/openai/types/image_model.py index 8ea486fbb6..4586fdebdf 100644 --- a/src/openai/types/image_model.py +++ b/src/openai/types/image_model.py @@ -4,4 +4,13 @@ __all__ = ["ImageModel"] -ImageModel: TypeAlias = Literal["gpt-image-1.5", "dall-e-2", "dall-e-3", "gpt-image-1", "gpt-image-1-mini"] +ImageModel: TypeAlias = Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-1.5", + "chatgpt-image-latest", + "dall-e-2", + "dall-e-3", +] diff --git a/src/openai/types/realtime/translations/__init__.py b/src/openai/types/realtime/translations/__init__.py new file mode 100644 index 0000000000..f8ee8b14b1 --- /dev/null +++ b/src/openai/types/realtime/translations/__init__.py @@ -0,0 +1,3 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 34120a287e..90929123c6 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -267,7 +267,18 @@ class ImageGeneration(BaseModel): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"], None] = None + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + None, + ] = None """The image generation model to use. Default: `gpt-image-1`.""" moderation: Optional[Literal["auto", "low"]] = None diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index c0f33c4513..8e6c6591a9 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -267,7 +267,17 @@ class ImageGeneration(TypedDict, total=False): Contains `image_url` (string, optional) and `file_id` (string, optional). """ - model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"]] + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + ] """The image generation model to use. Default: `gpt-image-1`.""" moderation: Literal["auto", "low"] diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 13cbc0acb7..6f6e700517 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -28,7 +28,7 @@ def test_method_create_variation(self, client: OpenAI) -> None: def test_method_create_variation_with_all_params(self, client: OpenAI) -> None: image = client.images.create_variation( image=b"Example data", - model="gpt-image-1.5", + model="gpt-image-1", n=1, response_format="url", size="1024x1024", @@ -76,7 +76,7 @@ def test_method_edit_with_all_params_overload_1(self, client: OpenAI) -> None: background="transparent", input_fidelity="high", mask=b"Example data", - model="gpt-image-1.5", + model="gpt-image-2", n=1, output_compression=100, output_format="png", @@ -133,7 +133,7 @@ def test_method_edit_with_all_params_overload_2(self, client: OpenAI) -> None: background="transparent", input_fidelity="high", mask=b"Example data", - model="gpt-image-1.5", + model="gpt-image-2", n=1, output_compression=100, output_format="png", @@ -184,7 +184,7 @@ def test_method_generate_with_all_params_overload_1(self, client: OpenAI) -> Non image = client.images.generate( prompt="A cute baby sea otter", background="transparent", - model="gpt-image-1.5", + model="gpt-image-2", moderation="low", n=1, output_compression=100, @@ -237,7 +237,7 @@ def test_method_generate_with_all_params_overload_2(self, client: OpenAI) -> Non prompt="A cute baby sea otter", stream=True, background="transparent", - model="gpt-image-1.5", + model="gpt-image-2", moderation="low", n=1, output_compression=100, @@ -293,7 +293,7 @@ async def test_method_create_variation(self, async_client: AsyncOpenAI) -> None: async def test_method_create_variation_with_all_params(self, async_client: AsyncOpenAI) -> None: image = await async_client.images.create_variation( image=b"Example data", - model="gpt-image-1.5", + model="gpt-image-1", n=1, response_format="url", size="1024x1024", @@ -341,7 +341,7 @@ async def test_method_edit_with_all_params_overload_1(self, async_client: AsyncO background="transparent", input_fidelity="high", mask=b"Example data", - model="gpt-image-1.5", + model="gpt-image-2", n=1, output_compression=100, output_format="png", @@ -398,7 +398,7 @@ async def test_method_edit_with_all_params_overload_2(self, async_client: AsyncO background="transparent", input_fidelity="high", mask=b"Example data", - model="gpt-image-1.5", + model="gpt-image-2", n=1, output_compression=100, output_format="png", @@ -449,7 +449,7 @@ async def test_method_generate_with_all_params_overload_1(self, async_client: As image = await async_client.images.generate( prompt="A cute baby sea otter", background="transparent", - model="gpt-image-1.5", + model="gpt-image-2", moderation="low", n=1, output_compression=100, @@ -502,7 +502,7 @@ async def test_method_generate_with_all_params_overload_2(self, async_client: As prompt="A cute baby sea otter", stream=True, background="transparent", - model="gpt-image-1.5", + model="gpt-image-2", moderation="low", n=1, output_compression=100, From 72bf67acbc9f030c20db3d5a1a74ea6d67d55f51 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 17:11:30 +0000 Subject: [PATCH 341/408] feat(api): manual updates --- .stats.yml | 4 +- src/openai/resources/images.py | 362 ++++++++++++------ src/openai/types/image_edit_params.py | 26 +- src/openai/types/image_generate_params.py | 29 +- .../types/realtime/translations/__init__.py | 3 - src/openai/types/responses/tool.py | 33 +- src/openai/types/responses/tool_param.py | 33 +- 7 files changed, 336 insertions(+), 154 deletions(-) delete mode 100644 src/openai/types/realtime/translations/__init__.py diff --git a/.stats.yml b/.stats.yml index 1dee1de615..e4dba3d576 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5ae2eda70e6a4a375842fb8bf3e88deab8e0aad1b3871b86e73274f131bb37ce.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5002c7ce1688cf372f6c268507494c5e6396f38db8d85d79029d949b7bb06fe3.yml openapi_spec_hash: 92713b0825f6b8760836778e6d27e837 -config_hash: c15a744b2771a3948032b2438475b330 +config_hash: c6cf65d9b19a16ce4313602a2204d48f diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 1b3ade12c9..6ac622ee1e 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -141,7 +141,7 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -172,9 +172,14 @@ def edit( characters for `dall-e-2`, and 32000 characters for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -219,9 +224,17 @@ def edit( generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) @@ -258,7 +271,7 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -292,9 +305,14 @@ def edit( for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -339,9 +357,17 @@ def edit( generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -374,7 +400,7 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -408,9 +434,14 @@ def edit( for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -455,9 +486,17 @@ def edit( generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -489,7 +528,7 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -557,10 +596,7 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, @@ -581,9 +617,14 @@ def generate( characters for `dall-e-3`. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -627,10 +668,17 @@ def generate( after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of - `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) @@ -670,10 +718,7 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -697,9 +742,14 @@ def generate( for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -743,10 +793,17 @@ def generate( after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of - `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -782,10 +839,7 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -809,9 +863,14 @@ def generate( for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -855,10 +914,17 @@ def generate( after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of - `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -893,10 +959,7 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, @@ -1059,7 +1122,7 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1090,9 +1153,14 @@ async def edit( characters for `dall-e-2`, and 32000 characters for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1137,9 +1205,17 @@ async def edit( generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. stream: Edit the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) @@ -1176,7 +1252,7 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1210,9 +1286,14 @@ async def edit( for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1257,9 +1338,17 @@ async def edit( generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -1292,7 +1381,7 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1326,9 +1415,14 @@ async def edit( for more information. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1373,9 +1467,17 @@ async def edit( generated. This parameter is only supported for `dall-e-2` (default is `url` for `dall-e-2`), as GPT image models always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, and one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. user: A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. @@ -1407,7 +1509,7 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1475,10 +1577,7 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, @@ -1499,9 +1598,14 @@ async def generate( characters for `dall-e-3`. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1545,10 +1649,17 @@ async def generate( after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of - `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. stream: Generate the image in streaming mode. Defaults to `false`. See the [Image generation guide](https://platform.openai.com/docs/guides/image-generation) @@ -1588,10 +1699,7 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1615,9 +1723,14 @@ async def generate( for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1661,10 +1774,17 @@ async def generate( after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of - `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -1700,10 +1820,7 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1727,9 +1844,14 @@ async def generate( for more information. This parameter is only supported for the GPT image models. background: Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -1773,10 +1895,17 @@ async def generate( after the image has been generated. This parameter isn't supported for the GPT image models, which always return base64-encoded images. - size: The size of the generated images. Must be one of `1024x1024`, `1536x1024` - (landscape), `1024x1536` (portrait), or `auto` (default value) for the GPT image - models, one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`, and one of - `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3`. + size: The size of the generated images. For `gpt-image-2` and + `gpt-image-2-2026-04-21`, arbitrary resolutions are supported as `WIDTHxHEIGHT` + strings, for example `1536x864`. Width and height must both be divisible by 16 + and the requested aspect ratio must be between 1:3 and 3:1. Resolutions above + `2560x1440` are experimental, and the maximum supported resolution is + `3840x2160`. The requested size must also satisfy the model's current pixel and + edge limits. The standard sizes `1024x1024`, `1536x1024`, and `1024x1536` are + supported by the GPT image models; `auto` is supported for models that allow + automatic sizing. For `dall-e-2`, use one of `256x256`, `512x512`, or + `1024x1024`. For `dall-e-3`, use one of `1024x1024`, `1792x1024`, or + `1024x1792`. style: The style of the generated images. This parameter is only supported for `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes the model to lean @@ -1811,10 +1940,7 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] - | Omit = omit, + size: Optional[str] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index ab70789fe6..3c0e41d572 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -34,9 +34,14 @@ class ImageEditParamsBase(TypedDict, total=False): background: Optional[Literal["transparent", "opaque", "auto"]] """ Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -109,12 +114,19 @@ class ImageEditParamsBase(TypedDict, total=False): base64-encoded images. """ - size: Optional[Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"]] + size: Optional[str] """The size of the generated images. - Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or - `auto` (default value) for the GPT image models, and one of `256x256`, - `512x512`, or `1024x1024` for `dall-e-2`. + For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are + supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height + must both be divisible by 16 and the requested aspect ratio must be between 1:3 + and 3:1. Resolutions above `2560x1440` are experimental, and the maximum + supported resolution is `3840x2160`. The requested size must also satisfy the + model's current pixel and edge limits. The standard sizes `1024x1024`, + `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is + supported for models that allow automatic sizing. For `dall-e-2`, use one of + `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, + `1792x1024`, or `1024x1792`. """ user: str diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 30a514cffb..4ac573ce61 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -21,9 +21,14 @@ class ImageGenerateParamsBase(TypedDict, total=False): background: Optional[Literal["transparent", "opaque", "auto"]] """ Allows to set transparency for the background of the generated image(s). This - parameter is only supported for the GPT image models. Must be one of - `transparent`, `opaque` or `auto` (default value). When `auto` is used, the - model will automatically determine the best background for the image. + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. If `transparent`, the output format needs to support transparency, so it should be set to either `png` (default value) or `webp`. @@ -95,15 +100,19 @@ class ImageGenerateParamsBase(TypedDict, total=False): models, which always return base64-encoded images. """ - size: Optional[ - Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] - ] + size: Optional[str] """The size of the generated images. - Must be one of `1024x1024`, `1536x1024` (landscape), `1024x1536` (portrait), or - `auto` (default value) for the GPT image models, one of `256x256`, `512x512`, or - `1024x1024` for `dall-e-2`, and one of `1024x1024`, `1792x1024`, or `1024x1792` - for `dall-e-3`. + For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are + supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height + must both be divisible by 16 and the requested aspect ratio must be between 1:3 + and 3:1. Resolutions above `2560x1440` are experimental, and the maximum + supported resolution is `3840x2160`. The requested size must also satisfy the + model's current pixel and edge limits. The standard sizes `1024x1024`, + `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is + supported for models that allow automatic sizing. For `dall-e-2`, use one of + `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, + `1792x1024`, or `1024x1792`. """ style: Optional[Literal["vivid", "natural"]] diff --git a/src/openai/types/realtime/translations/__init__.py b/src/openai/types/realtime/translations/__init__.py deleted file mode 100644 index f8ee8b14b1..0000000000 --- a/src/openai/types/realtime/translations/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 90929123c6..839ed26ba2 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -248,9 +248,19 @@ class ImageGeneration(BaseModel): """Whether to generate a new image or edit an existing image. Default: `auto`.""" background: Optional[Literal["transparent", "opaque", "auto"]] = None - """Background type for the generated image. - - One of `transparent`, `opaque`, or `auto`. Default: `auto`. + """ + Allows to set transparency for the background of the generated image(s). This + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. """ input_fidelity: Optional[Literal["high", "low"]] = None @@ -305,10 +315,19 @@ class ImageGeneration(BaseModel): One of `low`, `medium`, `high`, or `auto`. Default: `auto`. """ - size: Optional[Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] = None - """The size of the generated image. - - One of `1024x1024`, `1024x1536`, `1536x1024`, or `auto`. Default: `auto`. + size: Optional[str] = None + """The size of the generated images. + + For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are + supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height + must both be divisible by 16 and the requested aspect ratio must be between 1:3 + and 3:1. Resolutions above `2560x1440` are experimental, and the maximum + supported resolution is `3840x2160`. The requested size must also satisfy the + model's current pixel and edge limits. The standard sizes `1024x1024`, + `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is + supported for models that allow automatic sizing. For `dall-e-2`, use one of + `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, + `1792x1024`, or `1024x1792`. """ diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 8e6c6591a9..cdbf5bd1ff 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -248,9 +248,19 @@ class ImageGeneration(TypedDict, total=False): """Whether to generate a new image or edit an existing image. Default: `auto`.""" background: Literal["transparent", "opaque", "auto"] - """Background type for the generated image. - - One of `transparent`, `opaque`, or `auto`. Default: `auto`. + """ + Allows to set transparency for the background of the generated image(s). This + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. """ input_fidelity: Optional[Literal["high", "low"]] @@ -304,10 +314,19 @@ class ImageGeneration(TypedDict, total=False): One of `low`, `medium`, `high`, or `auto`. Default: `auto`. """ - size: Literal["1024x1024", "1024x1536", "1536x1024", "auto"] - """The size of the generated image. - - One of `1024x1024`, `1024x1536`, `1536x1024`, or `auto`. Default: `auto`. + size: str + """The size of the generated images. + + For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are + supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height + must both be divisible by 16 and the requested aspect ratio must be between 1:3 + and 3:1. Resolutions above `2560x1440` are experimental, and the maximum + supported resolution is `3840x2160`. The requested size must also satisfy the + model's current pixel and edge limits. The standard sizes `1024x1024`, + `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is + supported for models that allow automatic sizing. For `dall-e-2`, use one of + `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, + `1792x1024`, or `1024x1792`. """ From f9d339fcea63feaa1bdf918a4599f2b032c83517 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 08:58:03 +0000 Subject: [PATCH 342/408] docs(api): update top_logprobs parameter description across chat and responses --- .stats.yml | 4 +-- .../resources/chat/completions/completions.py | 30 +++++++++++-------- src/openai/resources/responses/responses.py | 30 +++++++++++-------- .../usage_audio_speeches_response.py | 6 ++++ .../usage_audio_transcriptions_response.py | 6 ++++ ...sage_code_interpreter_sessions_response.py | 6 ++++ .../usage_completions_response.py | 6 ++++ .../organization/usage_costs_response.py | 6 ++++ .../organization/usage_embeddings_response.py | 6 ++++ .../organization/usage_images_response.py | 6 ++++ .../usage_moderations_response.py | 6 ++++ .../usage_vector_stores_response.py | 6 ++++ .../chat/chat_completion_token_logprob.py | 3 +- .../types/chat/completion_create_params.py | 5 ++-- src/openai/types/responses/response.py | 5 ++-- .../types/responses/response_create_params.py | 5 ++-- .../responses/response_text_delta_event.py | 2 +- .../responses/response_text_done_event.py | 2 +- .../types/responses/responses_client_event.py | 5 ++-- .../responses/responses_client_event_param.py | 5 ++-- 20 files changed, 110 insertions(+), 40 deletions(-) diff --git a/.stats.yml b/.stats.yml index e4dba3d576..201feabd36 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5002c7ce1688cf372f6c268507494c5e6396f38db8d85d79029d949b7bb06fe3.yml -openapi_spec_hash: 92713b0825f6b8760836778e6d27e837 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-578515408e7ee6ac115f3768b358ff2cd4f5699cbfb527fe58b19806e7d1f713.yml +openapi_spec_hash: b2df68d6233a18b475590978f5682c04 config_hash: c6cf65d9b19a16ce4313602a2204d48f diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 7a551e2459..c85ac45cdb 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -516,8 +516,9 @@ def create( [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) or [function tools](https://platform.openai.com/docs/guides/function-calling). - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the @@ -822,8 +823,9 @@ def create( [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) or [function tools](https://platform.openai.com/docs/guides/function-calling). - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the @@ -1128,8 +1130,9 @@ def create( [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) or [function tools](https://platform.openai.com/docs/guides/function-calling). - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the @@ -2037,8 +2040,9 @@ async def create( [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) or [function tools](https://platform.openai.com/docs/guides/function-calling). - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the @@ -2343,8 +2347,9 @@ async def create( [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) or [function tools](https://platform.openai.com/docs/guides/function-calling). - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the @@ -2649,8 +2654,9 @@ async def create( [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) or [function tools](https://platform.openai.com/docs/guides/function-calling). - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. `logprobs` must be set to `true` if this parameter is used. top_p: An alternative to sampling with temperature, called nucleus sampling, where the diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index c0f9855bcf..4b8bd9af21 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -344,8 +344,9 @@ def create( [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use custom tools to call your own code. - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -593,8 +594,9 @@ def create( [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use custom tools to call your own code. - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -842,8 +844,9 @@ def create( [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use custom tools to call your own code. - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -2043,8 +2046,9 @@ async def create( [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use custom tools to call your own code. - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -2292,8 +2296,9 @@ async def create( [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use custom tools to call your own code. - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 @@ -2541,8 +2546,9 @@ async def create( [function calling](https://platform.openai.com/docs/guides/function-calling). You can also use custom tools to call your own code. - top_logprobs: An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. top_p: An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py index 90e17c89b0..e5fed36b03 100644 --- a/src/openai/types/admin/organization/usage_audio_speeches_response.py +++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py index abd7c3fb90..c5b77f34e1 100644 --- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py index 0cd6c2693c..aafda6f113 100644 --- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py +++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py index d37634bca5..f179149995 100644 --- a/src/openai/types/admin/organization/usage_completions_response.py +++ b/src/openai/types/admin/organization/usage_completions_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py index 68c1f639c1..fd1d344e26 100644 --- a/src/openai/types/admin/organization/usage_costs_response.py +++ b/src/openai/types/admin/organization/usage_costs_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py index 905c8f5c6e..adbc389d89 100644 --- a/src/openai/types/admin/organization/usage_embeddings_response.py +++ b/src/openai/types/admin/organization/usage_embeddings_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py index 55f8d80096..7c6a096c98 100644 --- a/src/openai/types/admin/organization/usage_images_response.py +++ b/src/openai/types/admin/organization/usage_images_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py index 87919b50a9..5e40ef1164 100644 --- a/src/openai/types/admin/organization/usage_moderations_response.py +++ b/src/openai/types/admin/organization/usage_moderations_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py index d3cd853653..089aa23119 100644 --- a/src/openai/types/admin/organization/usage_vector_stores_response.py +++ b/src/openai/types/admin/organization/usage_vector_stores_response.py @@ -353,6 +353,12 @@ class DataResultOrganizationCostsResult(BaseModel): costs result. """ + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + DataResult: TypeAlias = Annotated[ Union[ diff --git a/src/openai/types/chat/chat_completion_token_logprob.py b/src/openai/types/chat/chat_completion_token_logprob.py index c69e258910..4ce582366a 100644 --- a/src/openai/types/chat/chat_completion_token_logprob.py +++ b/src/openai/types/chat/chat_completion_token_logprob.py @@ -52,6 +52,5 @@ class ChatCompletionTokenLogprob(BaseModel): """List of the most likely tokens and their log probability, at this token position. - In rare cases, there may be fewer than the number of requested `top_logprobs` - returned. + The number of entries may be fewer than the requested `top_logprobs`. """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 0379ee0865..3c541b96b4 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -311,8 +311,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): top_logprobs: Optional[int] """ - An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. `logprobs` must be set to `true` if this parameter is used. """ diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 0d2491ea7c..dac3e09a89 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -276,8 +276,9 @@ class Response(BaseModel): top_logprobs: Optional[int] = None """ - An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. """ truncation: Optional[Literal["auto", "disabled"]] = None diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index a04495f40a..5f9b948ae9 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -251,8 +251,9 @@ class ResponseCreateParamsBase(TypedDict, total=False): top_logprobs: Optional[int] """ - An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. """ top_p: Optional[float] diff --git a/src/openai/types/responses/response_text_delta_event.py b/src/openai/types/responses/response_text_delta_event.py index 4f802abfd2..9b0b83de59 100644 --- a/src/openai/types/responses/response_text_delta_event.py +++ b/src/openai/types/responses/response_text_delta_event.py @@ -30,7 +30,7 @@ class Logprob(BaseModel): """The log probability of this token.""" top_logprobs: Optional[List[LogprobTopLogprob]] = None - """The log probability of the top 20 most likely tokens.""" + """The log probabilities of up to 20 of the most likely tokens.""" class ResponseTextDeltaEvent(BaseModel): diff --git a/src/openai/types/responses/response_text_done_event.py b/src/openai/types/responses/response_text_done_event.py index 75bd479870..3a202af67a 100644 --- a/src/openai/types/responses/response_text_done_event.py +++ b/src/openai/types/responses/response_text_done_event.py @@ -30,7 +30,7 @@ class Logprob(BaseModel): """The log probability of this token.""" top_logprobs: Optional[List[LogprobTopLogprob]] = None - """The log probability of the top 20 most likely tokens.""" + """The log probabilities of up to 20 of the most likely tokens.""" class ResponseTextDoneEvent(BaseModel): diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py index 5f9e73c61f..0a5dcb4ddd 100644 --- a/src/openai/types/responses/responses_client_event.py +++ b/src/openai/types/responses/responses_client_event.py @@ -293,8 +293,9 @@ class ResponsesClientEvent(BaseModel): top_logprobs: Optional[int] = None """ - An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. """ top_p: Optional[float] = None diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py index 249c812116..59d8f205ae 100644 --- a/src/openai/types/responses/responses_client_event_param.py +++ b/src/openai/types/responses/responses_client_event_param.py @@ -294,8 +294,9 @@ class ResponsesClientEventParam(TypedDict, total=False): top_logprobs: Optional[int] """ - An integer between 0 and 20 specifying the number of most likely tokens to - return at each token position, each with an associated log probability. + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. """ top_p: Optional[float] From a3b182d6d2c2e6fe1d53ca7550b2d43e0f8b2cd3 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Wed, 6 May 2026 10:06:24 -0400 Subject: [PATCH 343/408] chore: rename legacy python cli entrypoint --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7fbf3bd49b..38314d2869 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ Homepage = "https://github.com/openai/openai-python" Repository = "https://github.com/openai/openai-python" [project.scripts] -openai = "openai.cli:main" +openai-python = "openai.cli:main" [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] From 32f36e447d02c3124af8ab48fcc3537df2fed66e Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Wed, 6 May 2026 10:43:49 -0400 Subject: [PATCH 344/408] chore: remove legacy python cli --- pyproject.toml | 3 - src/openai/__main__.py | 3 - src/openai/cli/__init__.py | 1 - src/openai/cli/_api/__init__.py | 1 - src/openai/cli/_api/_main.py | 17 -- src/openai/cli/_api/audio.py | 108 --------- src/openai/cli/_api/chat/__init__.py | 13 -- src/openai/cli/_api/chat/completions.py | 160 -------------- src/openai/cli/_api/completions.py | 173 --------------- src/openai/cli/_api/files.py | 80 ------- src/openai/cli/_api/fine_tuning/__init__.py | 13 -- src/openai/cli/_api/fine_tuning/jobs.py | 170 -------------- src/openai/cli/_api/image.py | 139 ------------ src/openai/cli/_api/models.py | 45 ---- src/openai/cli/_cli.py | 233 -------------------- src/openai/cli/_errors.py | 21 -- src/openai/cli/_models.py | 17 -- src/openai/cli/_progress.py | 59 ----- src/openai/cli/_tools/__init__.py | 1 - src/openai/cli/_tools/_main.py | 17 -- src/openai/cli/_tools/fine_tunes.py | 63 ------ src/openai/cli/_tools/migrate.py | 164 -------------- src/openai/cli/_utils.py | 45 ---- 23 files changed, 1546 deletions(-) delete mode 100644 src/openai/__main__.py delete mode 100644 src/openai/cli/__init__.py delete mode 100644 src/openai/cli/_api/__init__.py delete mode 100644 src/openai/cli/_api/_main.py delete mode 100644 src/openai/cli/_api/audio.py delete mode 100644 src/openai/cli/_api/chat/__init__.py delete mode 100644 src/openai/cli/_api/chat/completions.py delete mode 100644 src/openai/cli/_api/completions.py delete mode 100644 src/openai/cli/_api/files.py delete mode 100644 src/openai/cli/_api/fine_tuning/__init__.py delete mode 100644 src/openai/cli/_api/fine_tuning/jobs.py delete mode 100644 src/openai/cli/_api/image.py delete mode 100644 src/openai/cli/_api/models.py delete mode 100644 src/openai/cli/_cli.py delete mode 100644 src/openai/cli/_errors.py delete mode 100644 src/openai/cli/_models.py delete mode 100644 src/openai/cli/_progress.py delete mode 100644 src/openai/cli/_tools/__init__.py delete mode 100644 src/openai/cli/_tools/_main.py delete mode 100644 src/openai/cli/_tools/fine_tunes.py delete mode 100644 src/openai/cli/_tools/migrate.py delete mode 100644 src/openai/cli/_utils.py diff --git a/pyproject.toml b/pyproject.toml index 38314d2869..1f7c145039 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,9 +42,6 @@ classifiers = [ Homepage = "https://github.com/openai/openai-python" Repository = "https://github.com/openai/openai-python" -[project.scripts] -openai-python = "openai.cli:main" - [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] realtime = ["websockets >= 13, < 16"] diff --git a/src/openai/__main__.py b/src/openai/__main__.py deleted file mode 100644 index 4e28416e10..0000000000 --- a/src/openai/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .cli import main - -main() diff --git a/src/openai/cli/__init__.py b/src/openai/cli/__init__.py deleted file mode 100644 index d453d5e179..0000000000 --- a/src/openai/cli/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._cli import main as main diff --git a/src/openai/cli/_api/__init__.py b/src/openai/cli/_api/__init__.py deleted file mode 100644 index 56a0260a6d..0000000000 --- a/src/openai/cli/_api/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._main import register_commands as register_commands diff --git a/src/openai/cli/_api/_main.py b/src/openai/cli/_api/_main.py deleted file mode 100644 index b04a3e52a4..0000000000 --- a/src/openai/cli/_api/_main.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from argparse import ArgumentParser - -from . import chat, audio, files, image, models, completions, fine_tuning - - -def register_commands(parser: ArgumentParser) -> None: - subparsers = parser.add_subparsers(help="All API subcommands") - - chat.register(subparsers) - image.register(subparsers) - audio.register(subparsers) - files.register(subparsers) - models.register(subparsers) - completions.register(subparsers) - fine_tuning.register(subparsers) diff --git a/src/openai/cli/_api/audio.py b/src/openai/cli/_api/audio.py deleted file mode 100644 index e7c3734e75..0000000000 --- a/src/openai/cli/_api/audio.py +++ /dev/null @@ -1,108 +0,0 @@ -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING, Any, Optional, cast -from argparse import ArgumentParser - -from .._utils import get_client, print_model -from ..._types import omit -from .._models import BaseModel -from .._progress import BufferReader -from ...types.audio import Transcription - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - # transcriptions - sub = subparser.add_parser("audio.transcriptions.create") - - # Required - sub.add_argument("-m", "--model", type=str, default="whisper-1") - sub.add_argument("-f", "--file", type=str, required=True) - # Optional - sub.add_argument("--response-format", type=str) - sub.add_argument("--language", type=str) - sub.add_argument("-t", "--temperature", type=float) - sub.add_argument("--prompt", type=str) - sub.set_defaults(func=CLIAudio.transcribe, args_model=CLITranscribeArgs) - - # translations - sub = subparser.add_parser("audio.translations.create") - - # Required - sub.add_argument("-f", "--file", type=str, required=True) - # Optional - sub.add_argument("-m", "--model", type=str, default="whisper-1") - sub.add_argument("--response-format", type=str) - # TODO: doesn't seem to be supported by the API - # sub.add_argument("--language", type=str) - sub.add_argument("-t", "--temperature", type=float) - sub.add_argument("--prompt", type=str) - sub.set_defaults(func=CLIAudio.translate, args_model=CLITranslationArgs) - - -class CLITranscribeArgs(BaseModel): - model: str - file: str - response_format: Optional[str] = None - language: Optional[str] = None - temperature: Optional[float] = None - prompt: Optional[str] = None - - -class CLITranslationArgs(BaseModel): - model: str - file: str - response_format: Optional[str] = None - language: Optional[str] = None - temperature: Optional[float] = None - prompt: Optional[str] = None - - -class CLIAudio: - @staticmethod - def transcribe(args: CLITranscribeArgs) -> None: - with open(args.file, "rb") as file_reader: - buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - - model = cast( - "Transcription | str", - get_client().audio.transcriptions.create( - file=(args.file, buffer_reader), - model=args.model, - language=args.language or omit, - temperature=args.temperature or omit, - prompt=args.prompt or omit, - # casts required because the API is typed for enums - # but we don't want to validate that here for forwards-compat - response_format=cast(Any, args.response_format), - ), - ) - if isinstance(model, str): - sys.stdout.write(model + "\n") - else: - print_model(model) - - @staticmethod - def translate(args: CLITranslationArgs) -> None: - with open(args.file, "rb") as file_reader: - buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - - model = cast( - "Transcription | str", - get_client().audio.translations.create( - file=(args.file, buffer_reader), - model=args.model, - temperature=args.temperature or omit, - prompt=args.prompt or omit, - # casts required because the API is typed for enums - # but we don't want to validate that here for forwards-compat - response_format=cast(Any, args.response_format), - ), - ) - if isinstance(model, str): - sys.stdout.write(model + "\n") - else: - print_model(model) diff --git a/src/openai/cli/_api/chat/__init__.py b/src/openai/cli/_api/chat/__init__.py deleted file mode 100644 index 87d971630a..0000000000 --- a/src/openai/cli/_api/chat/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING -from argparse import ArgumentParser - -from . import completions - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - completions.register(subparser) diff --git a/src/openai/cli/_api/chat/completions.py b/src/openai/cli/_api/chat/completions.py deleted file mode 100644 index 344eeff37c..0000000000 --- a/src/openai/cli/_api/chat/completions.py +++ /dev/null @@ -1,160 +0,0 @@ -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING, List, Optional, cast -from argparse import ArgumentParser -from typing_extensions import Literal, NamedTuple - -from ..._utils import get_client -from ..._models import BaseModel -from ...._streaming import Stream -from ....types.chat import ( - ChatCompletionRole, - ChatCompletionChunk, - CompletionCreateParams, -) -from ....types.chat.completion_create_params import ( - CompletionCreateParamsStreaming, - CompletionCreateParamsNonStreaming, -) - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("chat.completions.create") - - sub._action_groups.pop() - req = sub.add_argument_group("required arguments") - opt = sub.add_argument_group("optional arguments") - - req.add_argument( - "-g", - "--message", - action="append", - nargs=2, - metavar=("ROLE", "CONTENT"), - help="A message in `{role} {content}` format. Use this argument multiple times to add multiple messages.", - required=True, - ) - req.add_argument( - "-m", - "--model", - help="The model to use.", - required=True, - ) - - opt.add_argument( - "-n", - "--n", - help="How many completions to generate for the conversation.", - type=int, - ) - opt.add_argument("-M", "--max-tokens", help="The maximum number of tokens to generate.", type=int) - opt.add_argument( - "-t", - "--temperature", - help="""What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. - -Mutually exclusive with `top_p`.""", - type=float, - ) - opt.add_argument( - "-P", - "--top_p", - help="""An alternative to sampling with temperature, called nucleus sampling, where the considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10%% probability mass are considered. - - Mutually exclusive with `temperature`.""", - type=float, - ) - opt.add_argument( - "--stop", - help="A stop sequence at which to stop generating tokens for the message.", - ) - opt.add_argument("--stream", help="Stream messages as they're ready.", action="store_true") - sub.set_defaults(func=CLIChatCompletion.create, args_model=CLIChatCompletionCreateArgs) - - -class CLIMessage(NamedTuple): - role: ChatCompletionRole - content: str - - -class CLIChatCompletionCreateArgs(BaseModel): - message: List[CLIMessage] - model: str - n: Optional[int] = None - max_tokens: Optional[int] = None - temperature: Optional[float] = None - top_p: Optional[float] = None - stop: Optional[str] = None - stream: bool = False - - -class CLIChatCompletion: - @staticmethod - def create(args: CLIChatCompletionCreateArgs) -> None: - params: CompletionCreateParams = { - "model": args.model, - "messages": [ - {"role": cast(Literal["user"], message.role), "content": message.content} for message in args.message - ], - # type checkers are not good at inferring union types so we have to set stream afterwards - "stream": False, - } - if args.temperature is not None: - params["temperature"] = args.temperature - if args.stop is not None: - params["stop"] = args.stop - if args.top_p is not None: - params["top_p"] = args.top_p - if args.n is not None: - params["n"] = args.n - if args.stream: - params["stream"] = args.stream # type: ignore - if args.max_tokens is not None: - params["max_tokens"] = args.max_tokens - - if args.stream: - return CLIChatCompletion._stream_create(cast(CompletionCreateParamsStreaming, params)) - - return CLIChatCompletion._create(cast(CompletionCreateParamsNonStreaming, params)) - - @staticmethod - def _create(params: CompletionCreateParamsNonStreaming) -> None: - completion = get_client().chat.completions.create(**params) - should_print_header = len(completion.choices) > 1 - for choice in completion.choices: - if should_print_header: - sys.stdout.write("===== Chat Completion {} =====\n".format(choice.index)) - - content = choice.message.content if choice.message.content is not None else "None" - sys.stdout.write(content) - - if should_print_header or not content.endswith("\n"): - sys.stdout.write("\n") - - sys.stdout.flush() - - @staticmethod - def _stream_create(params: CompletionCreateParamsStreaming) -> None: - # cast is required for mypy - stream = cast( # pyright: ignore[reportUnnecessaryCast] - Stream[ChatCompletionChunk], get_client().chat.completions.create(**params) - ) - for chunk in stream: - should_print_header = len(chunk.choices) > 1 - for choice in chunk.choices: - if should_print_header: - sys.stdout.write("===== Chat Completion {} =====\n".format(choice.index)) - - content = choice.delta.content or "" - sys.stdout.write(content) - - if should_print_header: - sys.stdout.write("\n") - - sys.stdout.flush() - - sys.stdout.write("\n") diff --git a/src/openai/cli/_api/completions.py b/src/openai/cli/_api/completions.py deleted file mode 100644 index b22ecde9ef..0000000000 --- a/src/openai/cli/_api/completions.py +++ /dev/null @@ -1,173 +0,0 @@ -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING, Optional, cast -from argparse import ArgumentParser -from functools import partial - -from openai.types.completion import Completion - -from .._utils import get_client -from ..._types import Omittable, omit -from ..._utils import is_given -from .._errors import CLIError -from .._models import BaseModel -from ..._streaming import Stream - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("completions.create") - - # Required - sub.add_argument( - "-m", - "--model", - help="The model to use", - required=True, - ) - - # Optional - sub.add_argument("-p", "--prompt", help="An optional prompt to complete from") - sub.add_argument("--stream", help="Stream tokens as they're ready.", action="store_true") - sub.add_argument("-M", "--max-tokens", help="The maximum number of tokens to generate", type=int) - sub.add_argument( - "-t", - "--temperature", - help="""What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer. - -Mutually exclusive with `top_p`.""", - type=float, - ) - sub.add_argument( - "-P", - "--top_p", - help="""An alternative to sampling with temperature, called nucleus sampling, where the considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10%% probability mass are considered. - - Mutually exclusive with `temperature`.""", - type=float, - ) - sub.add_argument( - "-n", - "--n", - help="How many sub-completions to generate for each prompt.", - type=int, - ) - sub.add_argument( - "--logprobs", - help="Include the log probabilities on the `logprobs` most likely tokens, as well the chosen tokens. So for example, if `logprobs` is 10, the API will return a list of the 10 most likely tokens. If `logprobs` is 0, only the chosen tokens will have logprobs returned.", - type=int, - ) - sub.add_argument( - "--best_of", - help="Generates `best_of` completions server-side and returns the 'best' (the one with the highest log probability per token). Results cannot be streamed.", - type=int, - ) - sub.add_argument( - "--echo", - help="Echo back the prompt in addition to the completion", - action="store_true", - ) - sub.add_argument( - "--frequency_penalty", - help="Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.", - type=float, - ) - sub.add_argument( - "--presence_penalty", - help="Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.", - type=float, - ) - sub.add_argument("--suffix", help="The suffix that comes after a completion of inserted text.") - sub.add_argument("--stop", help="A stop sequence at which to stop generating tokens.") - sub.add_argument( - "--user", - help="A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.", - ) - # TODO: add support for logit_bias - sub.set_defaults(func=CLICompletions.create, args_model=CLICompletionCreateArgs) - - -class CLICompletionCreateArgs(BaseModel): - model: str - stream: bool = False - - prompt: Optional[str] = None - n: Omittable[int] = omit - stop: Omittable[str] = omit - user: Omittable[str] = omit - echo: Omittable[bool] = omit - suffix: Omittable[str] = omit - best_of: Omittable[int] = omit - top_p: Omittable[float] = omit - logprobs: Omittable[int] = omit - max_tokens: Omittable[int] = omit - temperature: Omittable[float] = omit - presence_penalty: Omittable[float] = omit - frequency_penalty: Omittable[float] = omit - - -class CLICompletions: - @staticmethod - def create(args: CLICompletionCreateArgs) -> None: - if is_given(args.n) and args.n > 1 and args.stream: - raise CLIError("Can't stream completions with n>1 with the current CLI") - - make_request = partial( - get_client().completions.create, - n=args.n, - echo=args.echo, - stop=args.stop, - user=args.user, - model=args.model, - top_p=args.top_p, - prompt=args.prompt, - suffix=args.suffix, - best_of=args.best_of, - logprobs=args.logprobs, - max_tokens=args.max_tokens, - temperature=args.temperature, - presence_penalty=args.presence_penalty, - frequency_penalty=args.frequency_penalty, - ) - - if args.stream: - return CLICompletions._stream_create( - # mypy doesn't understand the `partial` function but pyright does - cast(Stream[Completion], make_request(stream=True)) # pyright: ignore[reportUnnecessaryCast] - ) - - return CLICompletions._create(make_request()) - - @staticmethod - def _create(completion: Completion) -> None: - should_print_header = len(completion.choices) > 1 - for choice in completion.choices: - if should_print_header: - sys.stdout.write("===== Completion {} =====\n".format(choice.index)) - - sys.stdout.write(choice.text) - - if should_print_header or not choice.text.endswith("\n"): - sys.stdout.write("\n") - - sys.stdout.flush() - - @staticmethod - def _stream_create(stream: Stream[Completion]) -> None: - for completion in stream: - should_print_header = len(completion.choices) > 1 - for choice in sorted(completion.choices, key=lambda c: c.index): - if should_print_header: - sys.stdout.write("===== Chat Completion {} =====\n".format(choice.index)) - - sys.stdout.write(choice.text) - - if should_print_header: - sys.stdout.write("\n") - - sys.stdout.flush() - - sys.stdout.write("\n") diff --git a/src/openai/cli/_api/files.py b/src/openai/cli/_api/files.py deleted file mode 100644 index 5f3631b284..0000000000 --- a/src/openai/cli/_api/files.py +++ /dev/null @@ -1,80 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, cast -from argparse import ArgumentParser - -from .._utils import get_client, print_model -from .._models import BaseModel -from .._progress import BufferReader - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("files.create") - - sub.add_argument( - "-f", - "--file", - required=True, - help="File to upload", - ) - sub.add_argument( - "-p", - "--purpose", - help="Why are you uploading this file? (see https://platform.openai.com/docs/api-reference/ for purposes)", - required=True, - ) - sub.set_defaults(func=CLIFile.create, args_model=CLIFileCreateArgs) - - sub = subparser.add_parser("files.retrieve") - sub.add_argument("-i", "--id", required=True, help="The files ID") - sub.set_defaults(func=CLIFile.get, args_model=CLIFileCreateArgs) - - sub = subparser.add_parser("files.delete") - sub.add_argument("-i", "--id", required=True, help="The files ID") - sub.set_defaults(func=CLIFile.delete, args_model=CLIFileCreateArgs) - - sub = subparser.add_parser("files.list") - sub.set_defaults(func=CLIFile.list) - - -class CLIFileIDArgs(BaseModel): - id: str - - -class CLIFileCreateArgs(BaseModel): - file: str - purpose: str - - -class CLIFile: - @staticmethod - def create(args: CLIFileCreateArgs) -> None: - with open(args.file, "rb") as file_reader: - buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - - file = get_client().files.create( - file=(args.file, buffer_reader), - # casts required because the API is typed for enums - # but we don't want to validate that here for forwards-compat - purpose=cast(Any, args.purpose), - ) - print_model(file) - - @staticmethod - def get(args: CLIFileIDArgs) -> None: - file = get_client().files.retrieve(file_id=args.id) - print_model(file) - - @staticmethod - def delete(args: CLIFileIDArgs) -> None: - file = get_client().files.delete(file_id=args.id) - print_model(file) - - @staticmethod - def list() -> None: - files = get_client().files.list() - for file in files: - print_model(file) diff --git a/src/openai/cli/_api/fine_tuning/__init__.py b/src/openai/cli/_api/fine_tuning/__init__.py deleted file mode 100644 index 11a2dfccbd..0000000000 --- a/src/openai/cli/_api/fine_tuning/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING -from argparse import ArgumentParser - -from . import jobs - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - jobs.register(subparser) diff --git a/src/openai/cli/_api/fine_tuning/jobs.py b/src/openai/cli/_api/fine_tuning/jobs.py deleted file mode 100644 index a4e429108a..0000000000 --- a/src/openai/cli/_api/fine_tuning/jobs.py +++ /dev/null @@ -1,170 +0,0 @@ -from __future__ import annotations - -import json -from typing import TYPE_CHECKING -from argparse import ArgumentParser - -from ..._utils import get_client, print_model -from ...._types import Omittable, omit -from ...._utils import is_given -from ..._models import BaseModel -from ....pagination import SyncCursorPage -from ....types.fine_tuning import ( - FineTuningJob, - FineTuningJobEvent, -) - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("fine_tuning.jobs.create") - sub.add_argument( - "-m", - "--model", - help="The model to fine-tune.", - required=True, - ) - sub.add_argument( - "-F", - "--training-file", - help="The training file to fine-tune the model on.", - required=True, - ) - sub.add_argument( - "-H", - "--hyperparameters", - help="JSON string of hyperparameters to use for fine-tuning.", - type=str, - ) - sub.add_argument( - "-s", - "--suffix", - help="A suffix to add to the fine-tuned model name.", - ) - sub.add_argument( - "-V", - "--validation-file", - help="The validation file to use for fine-tuning.", - ) - sub.set_defaults(func=CLIFineTuningJobs.create, args_model=CLIFineTuningJobsCreateArgs) - - sub = subparser.add_parser("fine_tuning.jobs.retrieve") - sub.add_argument( - "-i", - "--id", - help="The ID of the fine-tuning job to retrieve.", - required=True, - ) - sub.set_defaults(func=CLIFineTuningJobs.retrieve, args_model=CLIFineTuningJobsRetrieveArgs) - - sub = subparser.add_parser("fine_tuning.jobs.list") - sub.add_argument( - "-a", - "--after", - help="Identifier for the last job from the previous pagination request. If provided, only jobs created after this job will be returned.", - ) - sub.add_argument( - "-l", - "--limit", - help="Number of fine-tuning jobs to retrieve.", - type=int, - ) - sub.set_defaults(func=CLIFineTuningJobs.list, args_model=CLIFineTuningJobsListArgs) - - sub = subparser.add_parser("fine_tuning.jobs.cancel") - sub.add_argument( - "-i", - "--id", - help="The ID of the fine-tuning job to cancel.", - required=True, - ) - sub.set_defaults(func=CLIFineTuningJobs.cancel, args_model=CLIFineTuningJobsCancelArgs) - - sub = subparser.add_parser("fine_tuning.jobs.list_events") - sub.add_argument( - "-i", - "--id", - help="The ID of the fine-tuning job to list events for.", - required=True, - ) - sub.add_argument( - "-a", - "--after", - help="Identifier for the last event from the previous pagination request. If provided, only events created after this event will be returned.", - ) - sub.add_argument( - "-l", - "--limit", - help="Number of fine-tuning job events to retrieve.", - type=int, - ) - sub.set_defaults(func=CLIFineTuningJobs.list_events, args_model=CLIFineTuningJobsListEventsArgs) - - -class CLIFineTuningJobsCreateArgs(BaseModel): - model: str - training_file: str - hyperparameters: Omittable[str] = omit - suffix: Omittable[str] = omit - validation_file: Omittable[str] = omit - - -class CLIFineTuningJobsRetrieveArgs(BaseModel): - id: str - - -class CLIFineTuningJobsListArgs(BaseModel): - after: Omittable[str] = omit - limit: Omittable[int] = omit - - -class CLIFineTuningJobsCancelArgs(BaseModel): - id: str - - -class CLIFineTuningJobsListEventsArgs(BaseModel): - id: str - after: Omittable[str] = omit - limit: Omittable[int] = omit - - -class CLIFineTuningJobs: - @staticmethod - def create(args: CLIFineTuningJobsCreateArgs) -> None: - hyperparameters = json.loads(str(args.hyperparameters)) if is_given(args.hyperparameters) else omit - fine_tuning_job: FineTuningJob = get_client().fine_tuning.jobs.create( - model=args.model, - training_file=args.training_file, - hyperparameters=hyperparameters, - suffix=args.suffix, - validation_file=args.validation_file, - ) - print_model(fine_tuning_job) - - @staticmethod - def retrieve(args: CLIFineTuningJobsRetrieveArgs) -> None: - fine_tuning_job: FineTuningJob = get_client().fine_tuning.jobs.retrieve(fine_tuning_job_id=args.id) - print_model(fine_tuning_job) - - @staticmethod - def list(args: CLIFineTuningJobsListArgs) -> None: - fine_tuning_jobs: SyncCursorPage[FineTuningJob] = get_client().fine_tuning.jobs.list( - after=args.after or omit, limit=args.limit or omit - ) - print_model(fine_tuning_jobs) - - @staticmethod - def cancel(args: CLIFineTuningJobsCancelArgs) -> None: - fine_tuning_job: FineTuningJob = get_client().fine_tuning.jobs.cancel(fine_tuning_job_id=args.id) - print_model(fine_tuning_job) - - @staticmethod - def list_events(args: CLIFineTuningJobsListEventsArgs) -> None: - fine_tuning_job_events: SyncCursorPage[FineTuningJobEvent] = get_client().fine_tuning.jobs.list_events( - fine_tuning_job_id=args.id, - after=args.after or omit, - limit=args.limit or omit, - ) - print_model(fine_tuning_job_events) diff --git a/src/openai/cli/_api/image.py b/src/openai/cli/_api/image.py deleted file mode 100644 index 1d0cf810c1..0000000000 --- a/src/openai/cli/_api/image.py +++ /dev/null @@ -1,139 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, cast -from argparse import ArgumentParser - -from .._utils import get_client, print_model -from ..._types import Omit, Omittable, omit -from .._models import BaseModel -from .._progress import BufferReader - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("images.generate") - sub.add_argument("-m", "--model", type=str) - sub.add_argument("-p", "--prompt", type=str, required=True) - sub.add_argument("-n", "--num-images", type=int, default=1) - sub.add_argument("-s", "--size", type=str, default="1024x1024", help="Size of the output image") - sub.add_argument("--response-format", type=str, default="url") - sub.set_defaults(func=CLIImage.create, args_model=CLIImageCreateArgs) - - sub = subparser.add_parser("images.edit") - sub.add_argument("-m", "--model", type=str) - sub.add_argument("-p", "--prompt", type=str, required=True) - sub.add_argument("-n", "--num-images", type=int, default=1) - sub.add_argument( - "-I", - "--image", - type=str, - required=True, - help="Image to modify. Should be a local path and a PNG encoded image.", - ) - sub.add_argument("-s", "--size", type=str, default="1024x1024", help="Size of the output image") - sub.add_argument("--response-format", type=str, default="url") - sub.add_argument( - "-M", - "--mask", - type=str, - required=False, - help="Path to a mask image. It should be the same size as the image you're editing and a RGBA PNG image. The Alpha channel acts as the mask.", - ) - sub.set_defaults(func=CLIImage.edit, args_model=CLIImageEditArgs) - - sub = subparser.add_parser("images.create_variation") - sub.add_argument("-m", "--model", type=str) - sub.add_argument("-n", "--num-images", type=int, default=1) - sub.add_argument( - "-I", - "--image", - type=str, - required=True, - help="Image to modify. Should be a local path and a PNG encoded image.", - ) - sub.add_argument("-s", "--size", type=str, default="1024x1024", help="Size of the output image") - sub.add_argument("--response-format", type=str, default="url") - sub.set_defaults(func=CLIImage.create_variation, args_model=CLIImageCreateVariationArgs) - - -class CLIImageCreateArgs(BaseModel): - prompt: str - num_images: int - size: str - response_format: str - model: Omittable[str] = omit - - -class CLIImageCreateVariationArgs(BaseModel): - image: str - num_images: int - size: str - response_format: str - model: Omittable[str] = omit - - -class CLIImageEditArgs(BaseModel): - image: str - num_images: int - size: str - response_format: str - prompt: str - mask: Omittable[str] = omit - model: Omittable[str] = omit - - -class CLIImage: - @staticmethod - def create(args: CLIImageCreateArgs) -> None: - image = get_client().images.generate( - model=args.model, - prompt=args.prompt, - n=args.num_images, - # casts required because the API is typed for enums - # but we don't want to validate that here for forwards-compat - size=cast(Any, args.size), - response_format=cast(Any, args.response_format), - ) - print_model(image) - - @staticmethod - def create_variation(args: CLIImageCreateVariationArgs) -> None: - with open(args.image, "rb") as file_reader: - buffer_reader = BufferReader(file_reader.read(), desc="Upload progress") - - image = get_client().images.create_variation( - model=args.model, - image=("image", buffer_reader), - n=args.num_images, - # casts required because the API is typed for enums - # but we don't want to validate that here for forwards-compat - size=cast(Any, args.size), - response_format=cast(Any, args.response_format), - ) - print_model(image) - - @staticmethod - def edit(args: CLIImageEditArgs) -> None: - with open(args.image, "rb") as file_reader: - buffer_reader = BufferReader(file_reader.read(), desc="Image upload progress") - - if isinstance(args.mask, Omit): - mask: Omittable[BufferReader] = omit - else: - with open(args.mask, "rb") as file_reader: - mask = BufferReader(file_reader.read(), desc="Mask progress") - - image = get_client().images.edit( - model=args.model, - prompt=args.prompt, - image=("image", buffer_reader), - n=args.num_images, - mask=("mask", mask) if not isinstance(mask, Omit) else mask, - # casts required because the API is typed for enums - # but we don't want to validate that here for forwards-compat - size=cast(Any, args.size), - response_format=cast(Any, args.response_format), - ) - print_model(image) diff --git a/src/openai/cli/_api/models.py b/src/openai/cli/_api/models.py deleted file mode 100644 index 017218fa6e..0000000000 --- a/src/openai/cli/_api/models.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING -from argparse import ArgumentParser - -from .._utils import get_client, print_model -from .._models import BaseModel - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("models.list") - sub.set_defaults(func=CLIModels.list) - - sub = subparser.add_parser("models.retrieve") - sub.add_argument("-i", "--id", required=True, help="The model ID") - sub.set_defaults(func=CLIModels.get, args_model=CLIModelIDArgs) - - sub = subparser.add_parser("models.delete") - sub.add_argument("-i", "--id", required=True, help="The model ID") - sub.set_defaults(func=CLIModels.delete, args_model=CLIModelIDArgs) - - -class CLIModelIDArgs(BaseModel): - id: str - - -class CLIModels: - @staticmethod - def get(args: CLIModelIDArgs) -> None: - model = get_client().models.retrieve(model=args.id) - print_model(model) - - @staticmethod - def delete(args: CLIModelIDArgs) -> None: - model = get_client().models.delete(model=args.id) - print_model(model) - - @staticmethod - def list() -> None: - models = get_client().models.list() - for model in models: - print_model(model) diff --git a/src/openai/cli/_cli.py b/src/openai/cli/_cli.py deleted file mode 100644 index d31196da50..0000000000 --- a/src/openai/cli/_cli.py +++ /dev/null @@ -1,233 +0,0 @@ -from __future__ import annotations - -import sys -import logging -import argparse -from typing import Any, List, Type, Optional -from typing_extensions import ClassVar - -import httpx -import pydantic - -import openai - -from . import _tools -from .. import _ApiType, __version__ -from ._api import register_commands -from ._utils import can_use_http2 -from ._errors import CLIError, display_error -from .._compat import PYDANTIC_V1, ConfigDict, model_parse -from .._models import BaseModel -from .._exceptions import APIError - -logger = logging.getLogger() -formatter = logging.Formatter("[%(asctime)s] %(message)s") -handler = logging.StreamHandler(sys.stderr) -handler.setFormatter(formatter) -logger.addHandler(handler) - - -class Arguments(BaseModel): - if PYDANTIC_V1: - - class Config(pydantic.BaseConfig): # type: ignore - extra: Any = pydantic.Extra.ignore # type: ignore - else: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="ignore", - ) - - verbosity: int - version: Optional[str] = None - - api_key: Optional[str] - api_base: Optional[str] - organization: Optional[str] - proxy: Optional[List[str]] - api_type: Optional[_ApiType] = None - api_version: Optional[str] = None - - # azure - azure_endpoint: Optional[str] = None - azure_ad_token: Optional[str] = None - - # internal, set by subparsers to parse their specific args - args_model: Optional[Type[BaseModel]] = None - - # internal, used so that subparsers can forward unknown arguments - unknown_args: List[str] = [] - allow_unknown_args: bool = False - - -def _build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description=None, prog="openai") - parser.add_argument( - "-v", - "--verbose", - action="count", - dest="verbosity", - default=0, - help="Set verbosity.", - ) - parser.add_argument("-b", "--api-base", help="What API base url to use.") - parser.add_argument("-k", "--api-key", help="What API key to use.") - parser.add_argument("-p", "--proxy", nargs="+", help="What proxy to use.") - parser.add_argument( - "-o", - "--organization", - help="Which organization to run as (will use your default organization if not specified)", - ) - parser.add_argument( - "-t", - "--api-type", - type=str, - choices=("openai", "azure"), - help="The backend API to call, must be `openai` or `azure`", - ) - parser.add_argument( - "--api-version", - help="The Azure API version, e.g. 'https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning'", - ) - - # azure - parser.add_argument( - "--azure-endpoint", - help="The Azure endpoint, e.g. 'https://endpoint.openai.azure.com'", - ) - parser.add_argument( - "--azure-ad-token", - help="A token from Azure Active Directory, https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id", - ) - - # prints the package version - parser.add_argument( - "-V", - "--version", - action="version", - version="%(prog)s " + __version__, - ) - - def help() -> None: - parser.print_help() - - parser.set_defaults(func=help) - - subparsers = parser.add_subparsers() - sub_api = subparsers.add_parser("api", help="Direct API calls") - - register_commands(sub_api) - - sub_tools = subparsers.add_parser("tools", help="Client side tools for convenience") - _tools.register_commands(sub_tools, subparsers) - - return parser - - -def main() -> int: - try: - _main() - except (APIError, CLIError, pydantic.ValidationError) as err: - display_error(err) - return 1 - except KeyboardInterrupt: - sys.stderr.write("\n") - return 1 - return 0 - - -def _parse_args(parser: argparse.ArgumentParser) -> tuple[argparse.Namespace, Arguments, list[str]]: - # argparse by default will strip out the `--` but we want to keep it for unknown arguments - if "--" in sys.argv: - idx = sys.argv.index("--") - known_args = sys.argv[1:idx] - unknown_args = sys.argv[idx:] - else: - known_args = sys.argv[1:] - unknown_args = [] - - parsed, remaining_unknown = parser.parse_known_args(known_args) - - # append any remaining unknown arguments from the initial parsing - remaining_unknown.extend(unknown_args) - - args = model_parse(Arguments, vars(parsed)) - if not args.allow_unknown_args: - # we have to parse twice to ensure any unknown arguments - # result in an error if that behaviour is desired - parser.parse_args() - - return parsed, args, remaining_unknown - - -def _main() -> None: - parser = _build_parser() - parsed, args, unknown = _parse_args(parser) - - if args.verbosity != 0: - sys.stderr.write("Warning: --verbosity isn't supported yet\n") - - proxies: dict[str, httpx.BaseTransport] = {} - if args.proxy is not None: - for proxy in args.proxy: - key = "https://" if proxy.startswith("https") else "http://" - if key in proxies: - raise CLIError(f"Multiple {key} proxies given - only the last one would be used") - - proxies[key] = httpx.HTTPTransport(proxy=httpx.Proxy(httpx.URL(proxy))) - - http_client = httpx.Client( - mounts=proxies or None, - http2=can_use_http2(), - ) - openai.http_client = http_client - - if args.organization: - openai.organization = args.organization - - if args.api_key: - openai.api_key = args.api_key - - if args.api_base: - openai.base_url = args.api_base - - # azure - if args.api_type is not None: - openai.api_type = args.api_type - - if args.azure_endpoint is not None: - openai.azure_endpoint = args.azure_endpoint - - if args.api_version is not None: - openai.api_version = args.api_version - - if args.azure_ad_token is not None: - openai.azure_ad_token = args.azure_ad_token - - try: - if args.args_model: - parsed.func( - model_parse( - args.args_model, - { - **{ - # we omit None values so that they can be defaulted to `NotGiven` - # and we'll strip it from the API request - key: value - for key, value in vars(parsed).items() - if value is not None - }, - "unknown_args": unknown, - }, - ) - ) - else: - parsed.func() - finally: - try: - http_client.close() - except Exception: - pass - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/src/openai/cli/_errors.py b/src/openai/cli/_errors.py deleted file mode 100644 index 7d0292dab2..0000000000 --- a/src/openai/cli/_errors.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -import sys - -import pydantic - -from ._utils import Colors, organization_info -from .._exceptions import APIError, OpenAIError - - -class CLIError(OpenAIError): ... - - -class SilentCLIError(CLIError): ... - - -def display_error(err: CLIError | APIError | pydantic.ValidationError) -> None: - if isinstance(err, SilentCLIError): - return - - sys.stderr.write("{}{}Error:{} {}\n".format(organization_info(), Colors.FAIL, Colors.ENDC, err)) diff --git a/src/openai/cli/_models.py b/src/openai/cli/_models.py deleted file mode 100644 index a88608961b..0000000000 --- a/src/openai/cli/_models.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Any -from typing_extensions import ClassVar - -import pydantic - -from .. import _models -from .._compat import PYDANTIC_V1, ConfigDict - - -class BaseModel(_models.BaseModel): - if PYDANTIC_V1: - - class Config(pydantic.BaseConfig): # type: ignore - extra: Any = pydantic.Extra.ignore # type: ignore - arbitrary_types_allowed: bool = True - else: - model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore", arbitrary_types_allowed=True) diff --git a/src/openai/cli/_progress.py b/src/openai/cli/_progress.py deleted file mode 100644 index 8a7f2525de..0000000000 --- a/src/openai/cli/_progress.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import annotations - -import io -from typing import Callable -from typing_extensions import override - - -class CancelledError(Exception): - def __init__(self, msg: str) -> None: - self.msg = msg - super().__init__(msg) - - @override - def __str__(self) -> str: - return self.msg - - __repr__ = __str__ - - -class BufferReader(io.BytesIO): - def __init__(self, buf: bytes = b"", desc: str | None = None) -> None: - super().__init__(buf) - self._len = len(buf) - self._progress = 0 - self._callback = progress(len(buf), desc=desc) - - def __len__(self) -> int: - return self._len - - @override - def read(self, n: int | None = -1) -> bytes: - chunk = io.BytesIO.read(self, n) - self._progress += len(chunk) - - try: - self._callback(self._progress) - except Exception as e: # catches exception from the callback - raise CancelledError("The upload was cancelled: {}".format(e)) from e - - return chunk - - -def progress(total: float, desc: str | None) -> Callable[[float], None]: - import tqdm - - meter = tqdm.tqdm(total=total, unit_scale=True, desc=desc) - - def incr(progress: float) -> None: - meter.n = progress - if progress == total: - meter.close() - else: - meter.refresh() - - return incr - - -def MB(i: int) -> int: - return int(i // 1024**2) diff --git a/src/openai/cli/_tools/__init__.py b/src/openai/cli/_tools/__init__.py deleted file mode 100644 index 56a0260a6d..0000000000 --- a/src/openai/cli/_tools/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._main import register_commands as register_commands diff --git a/src/openai/cli/_tools/_main.py b/src/openai/cli/_tools/_main.py deleted file mode 100644 index bd6cda408f..0000000000 --- a/src/openai/cli/_tools/_main.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING -from argparse import ArgumentParser - -from . import migrate, fine_tunes - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register_commands(parser: ArgumentParser, subparser: _SubParsersAction[ArgumentParser]) -> None: - migrate.register(subparser) - - namespaced = parser.add_subparsers(title="Tools", help="Convenience client side tools") - - fine_tunes.register(namespaced) diff --git a/src/openai/cli/_tools/fine_tunes.py b/src/openai/cli/_tools/fine_tunes.py deleted file mode 100644 index 2128b88952..0000000000 --- a/src/openai/cli/_tools/fine_tunes.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING -from argparse import ArgumentParser - -from .._models import BaseModel -from ...lib._validators import ( - get_validators, - write_out_file, - read_any_format, - apply_validators, - apply_necessary_remediation, -) - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("fine_tunes.prepare_data") - sub.add_argument( - "-f", - "--file", - required=True, - help="JSONL, JSON, CSV, TSV, TXT or XLSX file containing prompt-completion examples to be analyzed." - "This should be the local file path.", - ) - sub.add_argument( - "-q", - "--quiet", - required=False, - action="store_true", - help="Auto accepts all suggestions, without asking for user input. To be used within scripts.", - ) - sub.set_defaults(func=prepare_data, args_model=PrepareDataArgs) - - -class PrepareDataArgs(BaseModel): - file: str - - quiet: bool - - -def prepare_data(args: PrepareDataArgs) -> None: - sys.stdout.write("Analyzing...\n") - fname = args.file - auto_accept = args.quiet - df, remediation = read_any_format(fname) - apply_necessary_remediation(None, remediation) - - validators = get_validators() - - assert df is not None - - apply_validators( - df, - fname, - remediation, - validators, - auto_accept, - write_out_file_func=write_out_file, - ) diff --git a/src/openai/cli/_tools/migrate.py b/src/openai/cli/_tools/migrate.py deleted file mode 100644 index 841b777528..0000000000 --- a/src/openai/cli/_tools/migrate.py +++ /dev/null @@ -1,164 +0,0 @@ -from __future__ import annotations - -import os -import sys -import shutil -import tarfile -import platform -import subprocess -from typing import TYPE_CHECKING, List -from pathlib import Path -from argparse import ArgumentParser - -import httpx - -from .._errors import CLIError, SilentCLIError -from .._models import BaseModel - -if TYPE_CHECKING: - from argparse import _SubParsersAction - - -def register(subparser: _SubParsersAction[ArgumentParser]) -> None: - sub = subparser.add_parser("migrate") - sub.set_defaults(func=migrate, args_model=MigrateArgs, allow_unknown_args=True) - - sub = subparser.add_parser("grit") - sub.set_defaults(func=grit, args_model=GritArgs, allow_unknown_args=True) - - -class GritArgs(BaseModel): - # internal - unknown_args: List[str] = [] - - -def grit(args: GritArgs) -> None: - grit_path = install() - - try: - subprocess.check_call([grit_path, *args.unknown_args]) - except subprocess.CalledProcessError: - # stdout and stderr are forwarded by subprocess so an error will already - # have been displayed - raise SilentCLIError() from None - - -class MigrateArgs(BaseModel): - # internal - unknown_args: List[str] = [] - - -def migrate(args: MigrateArgs) -> None: - grit_path = install() - - try: - subprocess.check_call([grit_path, "apply", "openai", *args.unknown_args]) - except subprocess.CalledProcessError: - # stdout and stderr are forwarded by subprocess so an error will already - # have been displayed - raise SilentCLIError() from None - - -# handles downloading the Grit CLI until they provide their own PyPi package - -KEYGEN_ACCOUNT = "custodian-dev" - - -def _cache_dir() -> Path: - xdg = os.environ.get("XDG_CACHE_HOME") - if xdg is not None: - return Path(xdg) - - return Path.home() / ".cache" - - -def _debug(message: str) -> None: - if not os.environ.get("DEBUG"): - return - - sys.stdout.write(f"[DEBUG]: {message}\n") - - -def install() -> Path: - """Installs the Grit CLI and returns the location of the binary""" - if sys.platform == "win32": - raise CLIError("Windows is not supported yet in the migration CLI") - - _debug("Using Grit installer from GitHub") - - platform = "apple-darwin" if sys.platform == "darwin" else "unknown-linux-gnu" - - dir_name = _cache_dir() / "openai-python" - install_dir = dir_name / ".install" - target_dir = install_dir / "bin" - - target_path = target_dir / "grit" - temp_file = target_dir / "grit.tmp" - - if target_path.exists(): - _debug(f"{target_path} already exists") - sys.stdout.flush() - return target_path - - _debug(f"Using Grit CLI path: {target_path}") - - target_dir.mkdir(parents=True, exist_ok=True) - - if temp_file.exists(): - temp_file.unlink() - - arch = _get_arch() - _debug(f"Using architecture {arch}") - - file_name = f"grit-{arch}-{platform}" - download_url = f"https://github.com/getgrit/gritql/releases/latest/download/{file_name}.tar.gz" - - sys.stdout.write(f"Downloading Grit CLI from {download_url}\n") - with httpx.Client() as client: - download_response = client.get(download_url, follow_redirects=True) - if download_response.status_code != 200: - raise CLIError(f"Failed to download Grit CLI from {download_url}") - with open(temp_file, "wb") as file: - for chunk in download_response.iter_bytes(): - file.write(chunk) - - unpacked_dir = target_dir / "cli-bin" - unpacked_dir.mkdir(parents=True, exist_ok=True) - - with tarfile.open(temp_file, "r:gz") as archive: - if sys.version_info >= (3, 12): - archive.extractall(unpacked_dir, filter="data") - else: - archive.extractall(unpacked_dir) - - _move_files_recursively(unpacked_dir, target_dir) - - shutil.rmtree(unpacked_dir) - os.remove(temp_file) - os.chmod(target_path, 0o755) - - sys.stdout.flush() - - return target_path - - -def _move_files_recursively(source_dir: Path, target_dir: Path) -> None: - for item in source_dir.iterdir(): - if item.is_file(): - item.rename(target_dir / item.name) - elif item.is_dir(): - _move_files_recursively(item, target_dir) - - -def _get_arch() -> str: - architecture = platform.machine().lower() - - # Map the architecture names to Grit equivalents - arch_map = { - "x86_64": "x86_64", - "amd64": "x86_64", - "armv7l": "aarch64", - "arm64": "aarch64", - } - - return arch_map.get(architecture, architecture) diff --git a/src/openai/cli/_utils.py b/src/openai/cli/_utils.py deleted file mode 100644 index 673eed613c..0000000000 --- a/src/openai/cli/_utils.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -import sys - -import openai - -from .. import OpenAI, _load_client -from .._compat import model_json -from .._models import BaseModel - - -class Colors: - HEADER = "\033[95m" - OKBLUE = "\033[94m" - OKGREEN = "\033[92m" - WARNING = "\033[93m" - FAIL = "\033[91m" - ENDC = "\033[0m" - BOLD = "\033[1m" - UNDERLINE = "\033[4m" - - -def get_client() -> OpenAI: - return _load_client() - - -def organization_info() -> str: - organization = openai.organization - if organization is not None: - return "[organization={}] ".format(organization) - - return "" - - -def print_model(model: BaseModel) -> None: - sys.stdout.write(model_json(model, indent=2) + "\n") - - -def can_use_http2() -> bool: - try: - import h2 # type: ignore # noqa - except ImportError: - return False - - return True From d07d4a8b1611cea5cd2e345dbc09882d61bdcab4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 14:48:00 +0000 Subject: [PATCH 345/408] release: 2.35.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 20 ++++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7a1c4674e4..517b0cf98a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.34.0" + ".": "2.35.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 237a8c00a4..462cb2c24d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## 2.35.0 (2026-05-06) + +Full Changelog: [v2.34.0...v2.35.0](https://github.com/openai/openai-python/compare/v2.34.0...v2.35.0) + +### Features + +* **api:** launch realtime translate + update image 2 ([0ba55d7](https://github.com/openai/openai-python/commit/0ba55d7569565045426e1587906a70d5682a4bba)) +* **api:** manual updates ([72bf67a](https://github.com/openai/openai-python/commit/72bf67acbc9f030c20db3d5a1a74ea6d67d55f51)) + + +### Chores + +* remove legacy python cli ([32f36e4](https://github.com/openai/openai-python/commit/32f36e447d02c3124af8ab48fcc3537df2fed66e)) +* rename legacy python cli entrypoint ([a3b182d](https://github.com/openai/openai-python/commit/a3b182d6d2c2e6fe1d53ca7550b2d43e0f8b2cd3)) + + +### Documentation + +* **api:** update top_logprobs parameter description across chat and responses ([f9d339f](https://github.com/openai/openai-python/commit/f9d339fcea63feaa1bdf918a4599f2b032c83517)) + ## 2.34.0 (2026-05-04) Full Changelog: [v2.33.0...v2.34.0](https://github.com/openai/openai-python/compare/v2.33.0...v2.34.0) diff --git a/pyproject.toml b/pyproject.toml index 1f7c145039..1313a78507 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.34.0" +version = "2.35.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 857aeb7dff..e2c1c47531 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.34.0" # x-release-please-version +__version__ = "2.35.0" # x-release-please-version From 45ac986243ac038f12b47ada0578b25e6e94523b Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Wed, 6 May 2026 11:46:19 -0400 Subject: [PATCH 346/408] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 462cb2c24d..add3d25f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Full Changelog: [v2.34.0...v2.35.0](https://github.com/openai/openai-python/comp ### Features -* **api:** launch realtime translate + update image 2 ([0ba55d7](https://github.com/openai/openai-python/commit/0ba55d7569565045426e1587906a70d5682a4bba)) +* **api:** update image 2 ([0ba55d7](https://github.com/openai/openai-python/commit/0ba55d7569565045426e1587906a70d5682a4bba)) * **api:** manual updates ([72bf67a](https://github.com/openai/openai-python/commit/72bf67acbc9f030c20db3d5a1a74ea6d67d55f51)) From 99b9c422f385aa5126b6b525f066a9750e463e1e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 20:58:07 +0000 Subject: [PATCH 347/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 201feabd36..d0a34eab82 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-578515408e7ee6ac115f3768b358ff2cd4f5699cbfb527fe58b19806e7d1f713.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-feda36af0554900ba4ab2b74d82db104f65cfcddf1eef391c55fec82182414f3.yml openapi_spec_hash: b2df68d6233a18b475590978f5682c04 -config_hash: c6cf65d9b19a16ce4313602a2204d48f +config_hash: 632ca30e38686c90bef7622b1a2009ce From 14b8afce7f17a9748a3d2946eee4d2d0b180e1d8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 21:26:00 +0000 Subject: [PATCH 348/408] fix(api): fix imagegen `size` enum regression --- .stats.yml | 6 +- src/openai/resources/images.py | 80 ++++++++++++++++++----- src/openai/types/image_edit_params.py | 2 +- src/openai/types/image_generate_params.py | 6 +- src/openai/types/responses/tool.py | 2 +- src/openai/types/responses/tool_param.py | 2 +- tests/api_resources/test_images.py | 16 ++--- 7 files changed, 83 insertions(+), 31 deletions(-) diff --git a/.stats.yml b/.stats.yml index d0a34eab82..f3c3b26134 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-feda36af0554900ba4ab2b74d82db104f65cfcddf1eef391c55fec82182414f3.yml -openapi_spec_hash: b2df68d6233a18b475590978f5682c04 -config_hash: 632ca30e38686c90bef7622b1a2009ce +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-84d31411083374ec6cdb4a722f8b8b83c1230741157306b1ca7ba1a3cf246672.yml +openapi_spec_hash: 051fce676f959b8207e2317225ec4bdc +config_hash: a2916f18a94ff65c8116ca2fe3256f10 diff --git a/src/openai/resources/images.py b/src/openai/resources/images.py index 6ac622ee1e..8140b5863f 100644 --- a/src/openai/resources/images.py +++ b/src/openai/resources/images.py @@ -141,7 +141,8 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -271,7 +272,8 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -400,7 +402,8 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -528,7 +531,8 @@ def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -596,7 +600,12 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, @@ -718,7 +727,12 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -839,7 +853,12 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -959,7 +978,12 @@ def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, @@ -1122,7 +1146,8 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1252,7 +1277,8 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1381,7 +1407,8 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -1509,7 +1536,8 @@ async def edit( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] + | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1577,7 +1605,12 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, @@ -1699,7 +1732,12 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1820,7 +1858,12 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1940,7 +1983,12 @@ async def generate( partial_images: Optional[int] | Omit = omit, quality: Optional[Literal["standard", "hd", "low", "medium", "high", "auto"]] | Omit = omit, response_format: Optional[Literal["url", "b64_json"]] | Omit = omit, - size: Optional[str] | Omit = omit, + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] + | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, style: Optional[Literal["vivid", "natural"]] | Omit = omit, user: str | Omit = omit, diff --git a/src/openai/types/image_edit_params.py b/src/openai/types/image_edit_params.py index 3c0e41d572..1bd6dfd320 100644 --- a/src/openai/types/image_edit_params.py +++ b/src/openai/types/image_edit_params.py @@ -114,7 +114,7 @@ class ImageEditParamsBase(TypedDict, total=False): base64-encoded images. """ - size: Optional[str] + size: Union[str, Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"], None] """The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are diff --git a/src/openai/types/image_generate_params.py b/src/openai/types/image_generate_params.py index 4ac573ce61..0c5070b130 100644 --- a/src/openai/types/image_generate_params.py +++ b/src/openai/types/image_generate_params.py @@ -100,7 +100,11 @@ class ImageGenerateParamsBase(TypedDict, total=False): models, which always return base64-encoded images. """ - size: Optional[str] + size: Union[ + str, + Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"], + None, + ] """The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 839ed26ba2..92e6fb29a7 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -315,7 +315,7 @@ class ImageGeneration(BaseModel): One of `low`, `medium`, `high`, or `auto`. Default: `auto`. """ - size: Optional[str] = None + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"], None] = None """The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index cdbf5bd1ff..7a9a566b16 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -314,7 +314,7 @@ class ImageGeneration(TypedDict, total=False): One of `low`, `medium`, `high`, or `auto`. Default: `auto`. """ - size: str + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] """The size of the generated images. For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are diff --git a/tests/api_resources/test_images.py b/tests/api_resources/test_images.py index 6f6e700517..fa8054c01d 100644 --- a/tests/api_resources/test_images.py +++ b/tests/api_resources/test_images.py @@ -83,7 +83,7 @@ def test_method_edit_with_all_params_overload_1(self, client: OpenAI) -> None: partial_images=1, quality="high", response_format="url", - size="1024x1024", + size="256x256", stream=False, user="user-1234", ) @@ -140,7 +140,7 @@ def test_method_edit_with_all_params_overload_2(self, client: OpenAI) -> None: partial_images=1, quality="high", response_format="url", - size="1024x1024", + size="256x256", user="user-1234", ) image_stream.response.close() @@ -192,7 +192,7 @@ def test_method_generate_with_all_params_overload_1(self, client: OpenAI) -> Non partial_images=1, quality="medium", response_format="url", - size="1024x1024", + size="auto", stream=False, style="vivid", user="user-1234", @@ -245,7 +245,7 @@ def test_method_generate_with_all_params_overload_2(self, client: OpenAI) -> Non partial_images=1, quality="medium", response_format="url", - size="1024x1024", + size="auto", style="vivid", user="user-1234", ) @@ -348,7 +348,7 @@ async def test_method_edit_with_all_params_overload_1(self, async_client: AsyncO partial_images=1, quality="high", response_format="url", - size="1024x1024", + size="256x256", stream=False, user="user-1234", ) @@ -405,7 +405,7 @@ async def test_method_edit_with_all_params_overload_2(self, async_client: AsyncO partial_images=1, quality="high", response_format="url", - size="1024x1024", + size="256x256", user="user-1234", ) await image_stream.response.aclose() @@ -457,7 +457,7 @@ async def test_method_generate_with_all_params_overload_1(self, async_client: As partial_images=1, quality="medium", response_format="url", - size="1024x1024", + size="auto", stream=False, style="vivid", user="user-1234", @@ -510,7 +510,7 @@ async def test_method_generate_with_all_params_overload_2(self, async_client: As partial_images=1, quality="medium", response_format="url", - size="1024x1024", + size="auto", style="vivid", user="user-1234", ) From 5e8f09c2c8f65d2e93722270963f4a19a760736f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 21:26:53 +0000 Subject: [PATCH 349/408] release: 2.35.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 517b0cf98a..0c6e1a8623 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.35.0" + ".": "2.35.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index add3d25f2a..5b5e6afda7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.35.1 (2026-05-06) + +Full Changelog: [v2.35.0...v2.35.1](https://github.com/openai/openai-python/compare/v2.35.0...v2.35.1) + +### Bug Fixes + +* **api:** fix imagegen `size` enum regression ([4484653](https://github.com/openai/openai-python/commit/44846536bc3b02c393daa5bae70a85de04c7f621)) + ## 2.35.0 (2026-05-06) Full Changelog: [v2.34.0...v2.35.0](https://github.com/openai/openai-python/compare/v2.34.0...v2.35.0) diff --git a/pyproject.toml b/pyproject.toml index 1313a78507..cb8c01191d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.35.0" +version = "2.35.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e2c1c47531..7f151cf0cd 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.35.0" # x-release-please-version +__version__ = "2.35.1" # x-release-please-version From 12fc3e41f28ae433f41a88d71e1a29f6745c7640 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 21:54:11 +0000 Subject: [PATCH 350/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index f3c3b26134..ee0acc70c1 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-84d31411083374ec6cdb4a722f8b8b83c1230741157306b1ca7ba1a3cf246672.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-3c2b379de00a99101907866ecbe816bff4957f949691e641c6a4e809559386db.yml openapi_spec_hash: 051fce676f959b8207e2317225ec4bdc -config_hash: a2916f18a94ff65c8116ca2fe3256f10 +config_hash: 0970c9a530a7880eb4ca75ff96bcf61b From 19cda34ccc9fee540bad3523b223da3ae85f273c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 22:21:03 +0000 Subject: [PATCH 351/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ee0acc70c1..324998514f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-3c2b379de00a99101907866ecbe816bff4957f949691e641c6a4e809559386db.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-2388e437ea21ac4b945762c2c3d65d276f26014e7aee70e173e921193362b2b9.yml openapi_spec_hash: 051fce676f959b8207e2317225ec4bdc -config_hash: 0970c9a530a7880eb4ca75ff96bcf61b +config_hash: c8fd630a06acd84b4384186b926e3277 From 2f1dd2814f8d5af989dfa2077f9f748d94fd472a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 22:31:32 +0000 Subject: [PATCH 352/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 324998514f..8422e8dff5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-2388e437ea21ac4b945762c2c3d65d276f26014e7aee70e173e921193362b2b9.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-2f2e274f51df9ef4e7531b9e5c597bb9cd182fc56bb87ca617e2677a6abda72c.yml openapi_spec_hash: 051fce676f959b8207e2317225ec4bdc -config_hash: c8fd630a06acd84b4384186b926e3277 +config_hash: 5238a19104ff62c65ed1cb4b0aa46f4f From 13c639cc7d57e4fbd4406563511e15eeb88a54b2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 14:52:31 +0000 Subject: [PATCH 353/408] feat(api): manual updates --- .stats.yml | 6 +++--- src/openai/resources/realtime/api.md | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8422e8dff5..19bfc5fd04 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-2f2e274f51df9ef4e7531b9e5c597bb9cd182fc56bb87ca617e2677a6abda72c.yml -openapi_spec_hash: 051fce676f959b8207e2317225ec4bdc -config_hash: 5238a19104ff62c65ed1cb4b0aa46f4f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-08cb8ed18dfe4a9fa518e278576d3cfe5710cb5c22789cf80826c900569bcf56.yml +openapi_spec_hash: 20f820c94f54741b75d719f6a7371c12 +config_hash: f291a449469edfe61a28424e548899b2 diff --git a/src/openai/resources/realtime/api.md b/src/openai/resources/realtime/api.md index 1a178384db..1d1b23430b 100644 --- a/src/openai/resources/realtime/api.md +++ b/src/openai/resources/realtime/api.md @@ -77,6 +77,22 @@ from openai.types.realtime import ( RealtimeTranscriptionSessionAudioInput, RealtimeTranscriptionSessionAudioInputTurnDetection, RealtimeTranscriptionSessionCreateRequest, + RealtimeTranslationClientEvent, + RealtimeTranslationClientSecretCreateRequest, + RealtimeTranslationClientSecretCreateResponse, + RealtimeTranslationInputAudioBufferAppendEvent, + RealtimeTranslationInputTranscriptDeltaEvent, + RealtimeTranslationOutputAudioDeltaEvent, + RealtimeTranslationOutputTranscriptDeltaEvent, + RealtimeTranslationServerEvent, + RealtimeTranslationSession, + RealtimeTranslationSessionCloseEvent, + RealtimeTranslationSessionClosedEvent, + RealtimeTranslationSessionCreateRequest, + RealtimeTranslationSessionCreatedEvent, + RealtimeTranslationSessionUpdateEvent, + RealtimeTranslationSessionUpdateRequest, + RealtimeTranslationSessionUpdatedEvent, RealtimeTruncation, RealtimeTruncationRetentionRatio, ResponseAudioDeltaEvent, From 8fe0ab87e67eeb3cc27426b50093845229520f0e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 17:20:33 +0000 Subject: [PATCH 354/408] feat(api): realtime 2 --- .stats.yml | 6 ++-- src/openai/resources/realtime/api.md | 3 +- src/openai/resources/realtime/calls.py | 21 ++++++++++++ src/openai/types/realtime/__init__.py | 4 ++- .../types/realtime/audio_transcription.py | 15 +++++++-- .../realtime/audio_transcription_param.py | 15 +++++++-- .../types/realtime/call_accept_params.py | 11 +++++++ .../realtime/realtime_audio_config_input.py | 3 ++ .../realtime_audio_config_input_param.py | 3 ++ .../types/realtime/realtime_reasoning.py | 18 ++++++++++ .../realtime/realtime_reasoning_effort.py | 7 ++++ .../realtime/realtime_reasoning_param.py | 19 +++++++++++ .../realtime_response_create_params.py | 10 ++++++ .../realtime_response_create_params_param.py | 10 ++++++ .../realtime_session_client_secret.py | 22 ------------- .../realtime_session_create_request.py | 11 +++++++ .../realtime_session_create_request_param.py | 11 +++++++ .../realtime_session_create_response.py | 33 +++++++++---------- ...ltime_transcription_session_audio_input.py | 3 ++ ...transcription_session_audio_input_param.py | 3 ++ ...e_transcription_session_create_response.py | 3 +- ...me_transcription_session_turn_detection.py | 2 +- tests/api_resources/realtime/test_calls.py | 12 +++++++ .../realtime/test_client_secrets.py | 6 ++++ 24 files changed, 198 insertions(+), 53 deletions(-) create mode 100644 src/openai/types/realtime/realtime_reasoning.py create mode 100644 src/openai/types/realtime/realtime_reasoning_effort.py create mode 100644 src/openai/types/realtime/realtime_reasoning_param.py delete mode 100644 src/openai/types/realtime/realtime_session_client_secret.py diff --git a/.stats.yml b/.stats.yml index 19bfc5fd04..9b6dc7e58b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-08cb8ed18dfe4a9fa518e278576d3cfe5710cb5c22789cf80826c900569bcf56.yml -openapi_spec_hash: 20f820c94f54741b75d719f6a7371c12 -config_hash: f291a449469edfe61a28424e548899b2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-371f497afe4d6070f6e252e5febbe8f453c7058a8dff0c26a01b4d88442a4ac2.yml +openapi_spec_hash: d39f46e8fda45f77096448105efd175a +config_hash: b64135fff1fe9cf4069b9ecf59ae8b07 diff --git a/src/openai/resources/realtime/api.md b/src/openai/resources/realtime/api.md index 1d1b23430b..2be1b85cbf 100644 --- a/src/openai/resources/realtime/api.md +++ b/src/openai/resources/realtime/api.md @@ -58,6 +58,8 @@ from openai.types.realtime import ( RealtimeMcpToolCall, RealtimeMcpToolExecutionError, RealtimeMcphttpError, + RealtimeReasoning, + RealtimeReasoningEffort, RealtimeResponse, RealtimeResponseCreateAudioOutput, RealtimeResponseCreateMcpTool, @@ -130,7 +132,6 @@ Types: ```python from openai.types.realtime import ( - RealtimeSessionClientSecret, RealtimeSessionCreateResponse, RealtimeTranscriptionSessionCreateResponse, RealtimeTranscriptionSessionTurnDetection, diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index 7a4fcc0110..0674b2b010 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -28,6 +28,7 @@ call_reject_params, ) from ...types.responses.response_prompt_param import ResponsePromptParam +from ...types.realtime.realtime_reasoning_param import RealtimeReasoningParam from ...types.realtime.realtime_truncation_param import RealtimeTruncationParam from ...types.realtime.realtime_audio_config_param import RealtimeAudioConfigParam from ...types.realtime.realtime_tools_config_param import RealtimeToolsConfigParam @@ -121,6 +122,7 @@ def accept( Literal[ "gpt-realtime", "gpt-realtime-1.5", + "gpt-realtime-2", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -139,7 +141,9 @@ def accept( ] | Omit = omit, output_modalities: List[Literal["text", "audio"]] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, + reasoning: RealtimeReasoningParam | Omit = omit, tool_choice: RealtimeToolChoiceConfigParam | Omit = omit, tools: RealtimeToolsConfigParam | Omit = omit, tracing: Optional[RealtimeTracingConfigParam] | Omit = omit, @@ -188,9 +192,14 @@ def accept( can be used to make the model respond with text only. It is not possible to request both `text` and `audio` at the same time. + parallel_tool_calls: Whether the model may call multiple tools in parallel. Only supported by + reasoning Realtime models such as `gpt-realtime-2`. + prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + reasoning: Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`. + tool_choice: How the model chooses tools. Provide one of the string modes or force a specific function/MCP tool. @@ -245,7 +254,9 @@ def accept( "max_output_tokens": max_output_tokens, "model": model, "output_modalities": output_modalities, + "parallel_tool_calls": parallel_tool_calls, "prompt": prompt, + "reasoning": reasoning, "tool_choice": tool_choice, "tools": tools, "tracing": tracing, @@ -471,6 +482,7 @@ async def accept( Literal[ "gpt-realtime", "gpt-realtime-1.5", + "gpt-realtime-2", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -489,7 +501,9 @@ async def accept( ] | Omit = omit, output_modalities: List[Literal["text", "audio"]] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, + reasoning: RealtimeReasoningParam | Omit = omit, tool_choice: RealtimeToolChoiceConfigParam | Omit = omit, tools: RealtimeToolsConfigParam | Omit = omit, tracing: Optional[RealtimeTracingConfigParam] | Omit = omit, @@ -538,9 +552,14 @@ async def accept( can be used to make the model respond with text only. It is not possible to request both `text` and `audio` at the same time. + parallel_tool_calls: Whether the model may call multiple tools in parallel. Only supported by + reasoning Realtime models such as `gpt-realtime-2`. + prompt: Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + reasoning: Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`. + tool_choice: How the model chooses tools. Provide one of the string modes or force a specific function/MCP tool. @@ -595,7 +614,9 @@ async def accept( "max_output_tokens": max_output_tokens, "model": model, "output_modalities": output_modalities, + "parallel_tool_calls": parallel_tool_calls, "prompt": prompt, + "reasoning": reasoning, "tool_choice": tool_choice, "tools": tools, "tracing": tracing, diff --git a/src/openai/types/realtime/__init__.py b/src/openai/types/realtime/__init__.py index c2a141d727..d7a087ba9a 100644 --- a/src/openai/types/realtime/__init__.py +++ b/src/openai/types/realtime/__init__.py @@ -9,6 +9,7 @@ from .call_accept_params import CallAcceptParams as CallAcceptParams from .call_create_params import CallCreateParams as CallCreateParams from .call_reject_params import CallRejectParams as CallRejectParams +from .realtime_reasoning import RealtimeReasoning as RealtimeReasoning from .audio_transcription import AudioTranscription as AudioTranscription from .log_prob_properties import LogProbProperties as LogProbProperties from .realtime_truncation import RealtimeTruncation as RealtimeTruncation @@ -38,11 +39,13 @@ from .realtime_response_usage import RealtimeResponseUsage as RealtimeResponseUsage from .realtime_tracing_config import RealtimeTracingConfig as RealtimeTracingConfig from .mcp_list_tools_completed import McpListToolsCompleted as McpListToolsCompleted +from .realtime_reasoning_param import RealtimeReasoningParam as RealtimeReasoningParam from .realtime_response_status import RealtimeResponseStatus as RealtimeResponseStatus from .response_mcp_call_failed import ResponseMcpCallFailed as ResponseMcpCallFailed from .response_text_done_event import ResponseTextDoneEvent as ResponseTextDoneEvent from .audio_transcription_param import AudioTranscriptionParam as AudioTranscriptionParam from .rate_limits_updated_event import RateLimitsUpdatedEvent as RateLimitsUpdatedEvent +from .realtime_reasoning_effort import RealtimeReasoningEffort as RealtimeReasoningEffort from .realtime_truncation_param import RealtimeTruncationParam as RealtimeTruncationParam from .response_audio_done_event import ResponseAudioDoneEvent as ResponseAudioDoneEvent from .response_text_delta_event import ResponseTextDeltaEvent as ResponseTextDeltaEvent @@ -75,7 +78,6 @@ from .conversation_item_delete_event import ConversationItemDeleteEvent as ConversationItemDeleteEvent from .input_audio_buffer_clear_event import InputAudioBufferClearEvent as InputAudioBufferClearEvent from .realtime_mcp_approval_response import RealtimeMcpApprovalResponse as RealtimeMcpApprovalResponse -from .realtime_session_client_secret import RealtimeSessionClientSecret as RealtimeSessionClientSecret from .conversation_item_created_event import ConversationItemCreatedEvent as ConversationItemCreatedEvent from .conversation_item_deleted_event import ConversationItemDeletedEvent as ConversationItemDeletedEvent from .input_audio_buffer_append_event import InputAudioBufferAppendEvent as InputAudioBufferAppendEvent diff --git a/src/openai/types/realtime/audio_transcription.py b/src/openai/types/realtime/audio_transcription.py index 0a8c1371e0..45e2e388ca 100644 --- a/src/openai/types/realtime/audio_transcription.py +++ b/src/openai/types/realtime/audio_transcription.py @@ -9,6 +9,13 @@ class AudioTranscription(BaseModel): + delay: Optional[Literal["minimal", "low", "medium", "high", "xhigh"]] = None + """ + Controls how long the model waits before emitting transcription text. Higher + values can improve transcription accuracy at the cost of latency. Only supported + with `gpt-realtime-whisper` in GA Realtime sessions. + """ + language: Optional[str] = None """The language of the input audio. @@ -25,15 +32,16 @@ class AudioTranscription(BaseModel): "gpt-4o-mini-transcribe-2025-12-15", "gpt-4o-transcribe", "gpt-4o-transcribe-diarize", + "gpt-realtime-whisper", ], None, ] = None """The model to use for transcription. Current options are `whisper-1`, `gpt-4o-mini-transcribe`, - `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, and - `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need - diarization with speaker labels. + `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, + `gpt-4o-transcribe-diarize`, and `gpt-realtime-whisper`. Use + `gpt-4o-transcribe-diarize` when you need diarization with speaker labels. """ prompt: Optional[str] = None @@ -43,4 +51,5 @@ class AudioTranscription(BaseModel): [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the prompt is a free text string, for example "expect words related to technology". + Prompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions. """ diff --git a/src/openai/types/realtime/audio_transcription_param.py b/src/openai/types/realtime/audio_transcription_param.py index 7e60a003ce..9206921542 100644 --- a/src/openai/types/realtime/audio_transcription_param.py +++ b/src/openai/types/realtime/audio_transcription_param.py @@ -9,6 +9,13 @@ class AudioTranscriptionParam(TypedDict, total=False): + delay: Literal["minimal", "low", "medium", "high", "xhigh"] + """ + Controls how long the model waits before emitting transcription text. Higher + values can improve transcription accuracy at the cost of latency. Only supported + with `gpt-realtime-whisper` in GA Realtime sessions. + """ + language: str """The language of the input audio. @@ -25,14 +32,15 @@ class AudioTranscriptionParam(TypedDict, total=False): "gpt-4o-mini-transcribe-2025-12-15", "gpt-4o-transcribe", "gpt-4o-transcribe-diarize", + "gpt-realtime-whisper", ], ] """The model to use for transcription. Current options are `whisper-1`, `gpt-4o-mini-transcribe`, - `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, and - `gpt-4o-transcribe-diarize`. Use `gpt-4o-transcribe-diarize` when you need - diarization with speaker labels. + `gpt-4o-mini-transcribe-2025-12-15`, `gpt-4o-transcribe`, + `gpt-4o-transcribe-diarize`, and `gpt-realtime-whisper`. Use + `gpt-4o-transcribe-diarize` when you need diarization with speaker labels. """ prompt: str @@ -42,4 +50,5 @@ class AudioTranscriptionParam(TypedDict, total=False): [prompt is a list of keywords](https://platform.openai.com/docs/guides/speech-to-text#prompting). For `gpt-4o-transcribe` models (excluding `gpt-4o-transcribe-diarize`), the prompt is a free text string, for example "expect words related to technology". + Prompt is not supported with `gpt-realtime-whisper` in GA Realtime sessions. """ diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index 1baddbfc2c..b4a48fc8b5 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -5,6 +5,7 @@ from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from .realtime_reasoning_param import RealtimeReasoningParam from .realtime_truncation_param import RealtimeTruncationParam from .realtime_audio_config_param import RealtimeAudioConfigParam from .realtime_tools_config_param import RealtimeToolsConfigParam @@ -57,6 +58,7 @@ class CallAcceptParams(TypedDict, total=False): Literal[ "gpt-realtime", "gpt-realtime-1.5", + "gpt-realtime-2", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -83,12 +85,21 @@ class CallAcceptParams(TypedDict, total=False): only. It is not possible to request both `text` and `audio` at the same time. """ + parallel_tool_calls: bool + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + prompt: Optional[ResponsePromptParam] """ Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + reasoning: RealtimeReasoningParam + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + tool_choice: RealtimeToolChoiceConfigParam """How the model chooses tools. diff --git a/src/openai/types/realtime/realtime_audio_config_input.py b/src/openai/types/realtime/realtime_audio_config_input.py index 08e1b14601..ba7d211d0d 100644 --- a/src/openai/types/realtime/realtime_audio_config_input.py +++ b/src/openai/types/realtime/realtime_audio_config_input.py @@ -67,4 +67,7 @@ class RealtimeAudioConfigInput(BaseModel): trails off with "uhhm", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. """ diff --git a/src/openai/types/realtime/realtime_audio_config_input_param.py b/src/openai/types/realtime/realtime_audio_config_input_param.py index 73495e6cd3..5cea9f0efe 100644 --- a/src/openai/types/realtime/realtime_audio_config_input_param.py +++ b/src/openai/types/realtime/realtime_audio_config_input_param.py @@ -69,4 +69,7 @@ class RealtimeAudioConfigInputParam(TypedDict, total=False): trails off with "uhhm", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. """ diff --git a/src/openai/types/realtime/realtime_reasoning.py b/src/openai/types/realtime/realtime_reasoning.py new file mode 100644 index 0000000000..5d49c9bd0b --- /dev/null +++ b/src/openai/types/realtime/realtime_reasoning.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel +from .realtime_reasoning_effort import RealtimeReasoningEffort + +__all__ = ["RealtimeReasoning"] + + +class RealtimeReasoning(BaseModel): + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + effort: Optional[RealtimeReasoningEffort] = None + """ + Constrains effort on reasoning for reasoning-capable Realtime models such as + `gpt-realtime-2`. + """ diff --git a/src/openai/types/realtime/realtime_reasoning_effort.py b/src/openai/types/realtime/realtime_reasoning_effort.py new file mode 100644 index 0000000000..dcf9e303a7 --- /dev/null +++ b/src/openai/types/realtime/realtime_reasoning_effort.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["RealtimeReasoningEffort"] + +RealtimeReasoningEffort: TypeAlias = Literal["minimal", "low", "medium", "high", "xhigh"] diff --git a/src/openai/types/realtime/realtime_reasoning_param.py b/src/openai/types/realtime/realtime_reasoning_param.py new file mode 100644 index 0000000000..f6de89c99a --- /dev/null +++ b/src/openai/types/realtime/realtime_reasoning_param.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .realtime_reasoning_effort import RealtimeReasoningEffort + +__all__ = ["RealtimeReasoningParam"] + + +class RealtimeReasoningParam(TypedDict, total=False): + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + + effort: RealtimeReasoningEffort + """ + Constrains effort on reasoning for reasoning-capable Realtime models such as + `gpt-realtime-2`. + """ diff --git a/src/openai/types/realtime/realtime_response_create_params.py b/src/openai/types/realtime/realtime_response_create_params.py index deec8c9280..8e20168e34 100644 --- a/src/openai/types/realtime/realtime_response_create_params.py +++ b/src/openai/types/realtime/realtime_response_create_params.py @@ -6,6 +6,7 @@ from ..._models import BaseModel from ..shared.metadata import Metadata from .conversation_item import ConversationItem +from .realtime_reasoning import RealtimeReasoning from .realtime_function_tool import RealtimeFunctionTool from ..responses.response_prompt import ResponsePrompt from ..responses.tool_choice_mcp import ToolChoiceMcp @@ -84,12 +85,21 @@ class RealtimeResponseCreateParams(BaseModel): model. """ + parallel_tool_calls: Optional[bool] = None + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + prompt: Optional[ResponsePrompt] = None """ Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + reasoning: Optional[RealtimeReasoning] = None + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + tool_choice: Optional[ToolChoice] = None """How the model chooses tools. diff --git a/src/openai/types/realtime/realtime_response_create_params_param.py b/src/openai/types/realtime/realtime_response_create_params_param.py index caad5bc900..aafa3d4e25 100644 --- a/src/openai/types/realtime/realtime_response_create_params_param.py +++ b/src/openai/types/realtime/realtime_response_create_params_param.py @@ -7,6 +7,7 @@ from ..shared_params.metadata import Metadata from .conversation_item_param import ConversationItemParam +from .realtime_reasoning_param import RealtimeReasoningParam from .realtime_function_tool_param import RealtimeFunctionToolParam from ..responses.tool_choice_options import ToolChoiceOptions from ..responses.response_prompt_param import ResponsePromptParam @@ -85,12 +86,21 @@ class RealtimeResponseCreateParamsParam(TypedDict, total=False): model. """ + parallel_tool_calls: bool + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + prompt: Optional[ResponsePromptParam] """ Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + reasoning: RealtimeReasoningParam + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + tool_choice: ToolChoice """How the model chooses tools. diff --git a/src/openai/types/realtime/realtime_session_client_secret.py b/src/openai/types/realtime/realtime_session_client_secret.py deleted file mode 100644 index 13a12f5502..0000000000 --- a/src/openai/types/realtime/realtime_session_client_secret.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["RealtimeSessionClientSecret"] - - -class RealtimeSessionClientSecret(BaseModel): - """Ephemeral key returned by the API.""" - - expires_at: int - """Timestamp for when the token expires. - - Currently, all tokens expire after one minute. - """ - - value: str - """ - Ephemeral key usable in client environments to authenticate connections to the - Realtime API. Use this in client-side environments rather than a standard API - token, which should only be used server-side. - """ diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index 163a0d16d8..cf681e99a1 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -4,6 +4,7 @@ from typing_extensions import Literal from ..._models import BaseModel +from .realtime_reasoning import RealtimeReasoning from .realtime_truncation import RealtimeTruncation from .realtime_audio_config import RealtimeAudioConfig from .realtime_tools_config import RealtimeToolsConfig @@ -58,6 +59,7 @@ class RealtimeSessionCreateRequest(BaseModel): Literal[ "gpt-realtime", "gpt-realtime-1.5", + "gpt-realtime-2", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -85,12 +87,21 @@ class RealtimeSessionCreateRequest(BaseModel): only. It is not possible to request both `text` and `audio` at the same time. """ + parallel_tool_calls: Optional[bool] = None + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + prompt: Optional[ResponsePrompt] = None """ Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + reasoning: Optional[RealtimeReasoning] = None + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + tool_choice: Optional[RealtimeToolChoiceConfig] = None """How the model chooses tools. diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index 19c73b909b..ab7de47c3c 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -5,6 +5,7 @@ from typing import List, Union, Optional from typing_extensions import Literal, Required, TypedDict +from .realtime_reasoning_param import RealtimeReasoningParam from .realtime_truncation_param import RealtimeTruncationParam from .realtime_audio_config_param import RealtimeAudioConfigParam from .realtime_tools_config_param import RealtimeToolsConfigParam @@ -59,6 +60,7 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): Literal[ "gpt-realtime", "gpt-realtime-1.5", + "gpt-realtime-2", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -85,12 +87,21 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): only. It is not possible to request both `text` and `audio` at the same time. """ + parallel_tool_calls: bool + """Whether the model may call multiple tools in parallel. + + Only supported by reasoning Realtime models such as `gpt-realtime-2`. + """ + prompt: Optional[ResponsePromptParam] """ Reference to a prompt template and its variables. [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + reasoning: RealtimeReasoningParam + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + tool_choice: RealtimeToolChoiceConfigParam """How the model chooses tools. diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index e2ed5ddce5..7193eafcc1 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -5,6 +5,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel +from .realtime_reasoning import RealtimeReasoning from .audio_transcription import AudioTranscription from .realtime_truncation import RealtimeTruncation from .noise_reduction_type import NoiseReductionType @@ -13,7 +14,6 @@ from ..responses.response_prompt import ResponsePrompt from ..responses.tool_choice_mcp import ToolChoiceMcp from ..responses.tool_choice_options import ToolChoiceOptions -from .realtime_session_client_secret import RealtimeSessionClientSecret from ..responses.tool_choice_function import ToolChoiceFunction __all__ = [ @@ -176,16 +176,6 @@ class AudioInput(BaseModel): """ transcription: Optional[AudioTranscription] = None - """ - Configuration for input audio transcription, defaults to off and can be set to - `null` to turn off once on. Input audio transcription is not native to the - model, since the model consumes audio directly. Transcription runs - asynchronously through - [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) - and should be treated as guidance of input audio content rather than precisely - what the model heard. The client can optionally set the language and prompt for - transcription, these offer additional guidance to the transcription service. - """ turn_detection: Optional[AudioInputTurnDetection] = None """Configuration for turn detection, ether Server VAD or Semantic VAD. @@ -202,6 +192,9 @@ class AudioInput(BaseModel): trails off with "uhhm", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. """ @@ -414,14 +407,13 @@ class TracingTracingConfiguration(BaseModel): class RealtimeSessionCreateResponse(BaseModel): - """A new Realtime session configuration, with an ephemeral key. + """A Realtime session configuration object.""" - Default TTL - for keys is one minute. - """ + id: str + """Unique identifier for the session that looks like `sess_1234567890abcdef`.""" - client_secret: RealtimeSessionClientSecret - """Ephemeral key returned by the API.""" + object: Literal["realtime.session"] + """The object type. Always `realtime.session`.""" type: Literal["realtime"] """The type of session to create. Always `realtime` for the Realtime API.""" @@ -429,6 +421,9 @@ class RealtimeSessionCreateResponse(BaseModel): audio: Optional[Audio] = None """Configuration for input and output audio.""" + expires_at: Optional[int] = None + """Expiration timestamp for the session, in seconds since epoch.""" + include: Optional[List[Literal["item.input_audio_transcription.logprobs"]]] = None """Additional fields to include in server outputs. @@ -464,6 +459,7 @@ class RealtimeSessionCreateResponse(BaseModel): Literal[ "gpt-realtime", "gpt-realtime-1.5", + "gpt-realtime-2", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -497,6 +493,9 @@ class RealtimeSessionCreateResponse(BaseModel): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ + reasoning: Optional[RealtimeReasoning] = None + """Configuration for reasoning-capable Realtime models such as `gpt-realtime-2`.""" + tool_choice: Optional[ToolChoice] = None """How the model chooses tools. diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input.py b/src/openai/types/realtime/realtime_transcription_session_audio_input.py index 80ff223590..e6f044bc22 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input.py @@ -69,4 +69,7 @@ class RealtimeTranscriptionSessionAudioInput(BaseModel): trails off with "uhhm", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. """ diff --git a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py index dd908c72f6..cacb38dc22 100644 --- a/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py +++ b/src/openai/types/realtime/realtime_transcription_session_audio_input_param.py @@ -71,4 +71,7 @@ class RealtimeTranscriptionSessionAudioInputParam(TypedDict, total=False): trails off with "uhhm", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency. + + For `gpt-realtime-whisper` transcription sessions, turn detection must be set to + `null`; VAD is not supported. """ diff --git a/src/openai/types/realtime/realtime_transcription_session_create_response.py b/src/openai/types/realtime/realtime_transcription_session_create_response.py index 6ca6c3808b..68f7e71a54 100644 --- a/src/openai/types/realtime/realtime_transcription_session_create_response.py +++ b/src/openai/types/realtime/realtime_transcription_session_create_response.py @@ -31,14 +31,13 @@ class AudioInput(BaseModel): """Configuration for input audio noise reduction.""" transcription: Optional[AudioTranscription] = None - """Configuration of the transcription model.""" turn_detection: Optional[RealtimeTranscriptionSessionTurnDetection] = None """Configuration for turn detection. Can be set to `null` to turn off. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user - speech. + speech. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported. """ diff --git a/src/openai/types/realtime/realtime_transcription_session_turn_detection.py b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py index 8dacd60a07..0f3d5b1c00 100644 --- a/src/openai/types/realtime/realtime_transcription_session_turn_detection.py +++ b/src/openai/types/realtime/realtime_transcription_session_turn_detection.py @@ -12,7 +12,7 @@ class RealtimeTranscriptionSessionTurnDetection(BaseModel): Can be set to `null` to turn off. Server VAD means that the model will detect the start and end of speech based on - audio volume and respond at the end of user speech. + audio volume and respond at the end of user speech. For `gpt-realtime-whisper`, this must be `null`; VAD is not supported. """ prefix_padding_ms: Optional[int] = None diff --git a/tests/api_resources/realtime/test_calls.py b/tests/api_resources/realtime/test_calls.py index 43ab9afe01..3d87a77f3b 100644 --- a/tests/api_resources/realtime/test_calls.py +++ b/tests/api_resources/realtime/test_calls.py @@ -47,6 +47,7 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou }, "noise_reduction": {"type": "near_field"}, "transcription": { + "delay": "minimal", "language": "language", "model": "whisper-1", "prompt": "prompt", @@ -75,11 +76,13 @@ def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRou "max_output_tokens": "inf", "model": "gpt-realtime", "output_modalities": ["text"], + "parallel_tool_calls": True, "prompt": { "id": "id", "variables": {"foo": "string"}, "version": "version", }, + "reasoning": {"effort": "minimal"}, "tool_choice": "none", "tools": [ { @@ -146,6 +149,7 @@ def test_method_accept_with_all_params(self, client: OpenAI) -> None: }, "noise_reduction": {"type": "near_field"}, "transcription": { + "delay": "minimal", "language": "language", "model": "whisper-1", "prompt": "prompt", @@ -174,11 +178,13 @@ def test_method_accept_with_all_params(self, client: OpenAI) -> None: max_output_tokens="inf", model="gpt-realtime", output_modalities=["text"], + parallel_tool_calls=True, prompt={ "id": "id", "variables": {"foo": "string"}, "version": "version", }, + reasoning={"effort": "minimal"}, tool_choice="none", tools=[ { @@ -385,6 +391,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re }, "noise_reduction": {"type": "near_field"}, "transcription": { + "delay": "minimal", "language": "language", "model": "whisper-1", "prompt": "prompt", @@ -413,11 +420,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI, re "max_output_tokens": "inf", "model": "gpt-realtime", "output_modalities": ["text"], + "parallel_tool_calls": True, "prompt": { "id": "id", "variables": {"foo": "string"}, "version": "version", }, + "reasoning": {"effort": "minimal"}, "tool_choice": "none", "tools": [ { @@ -484,6 +493,7 @@ async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> }, "noise_reduction": {"type": "near_field"}, "transcription": { + "delay": "minimal", "language": "language", "model": "whisper-1", "prompt": "prompt", @@ -512,11 +522,13 @@ async def test_method_accept_with_all_params(self, async_client: AsyncOpenAI) -> max_output_tokens="inf", model="gpt-realtime", output_modalities=["text"], + parallel_tool_calls=True, prompt={ "id": "id", "variables": {"foo": "string"}, "version": "version", }, + reasoning={"effort": "minimal"}, tool_choice="none", tools=[ { diff --git a/tests/api_resources/realtime/test_client_secrets.py b/tests/api_resources/realtime/test_client_secrets.py index a354019eac..c8ec8f3d3b 100644 --- a/tests/api_resources/realtime/test_client_secrets.py +++ b/tests/api_resources/realtime/test_client_secrets.py @@ -39,6 +39,7 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: }, "noise_reduction": {"type": "near_field"}, "transcription": { + "delay": "minimal", "language": "language", "model": "whisper-1", "prompt": "prompt", @@ -67,11 +68,13 @@ def test_method_create_with_all_params(self, client: OpenAI) -> None: "max_output_tokens": "inf", "model": "gpt-realtime", "output_modalities": ["text"], + "parallel_tool_calls": True, "prompt": { "id": "id", "variables": {"foo": "string"}, "version": "version", }, + "reasoning": {"effort": "minimal"}, "tool_choice": "none", "tools": [ { @@ -135,6 +138,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> }, "noise_reduction": {"type": "near_field"}, "transcription": { + "delay": "minimal", "language": "language", "model": "whisper-1", "prompt": "prompt", @@ -163,11 +167,13 @@ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> "max_output_tokens": "inf", "model": "gpt-realtime", "output_modalities": ["text"], + "parallel_tool_calls": True, "prompt": { "id": "id", "variables": {"foo": "string"}, "version": "version", }, + "reasoning": {"effort": "minimal"}, "tool_choice": "none", "tools": [ { From ff683ffbeba94fc01d93966b774c05a3471f2495 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 17:24:54 +0000 Subject: [PATCH 355/408] release: 2.36.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0c6e1a8623..986390db98 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.35.1" + ".": "2.36.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b5e6afda7..af067330f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 2.36.0 (2026-05-07) + +Full Changelog: [v2.35.1...v2.36.0](https://github.com/openai/openai-python/compare/v2.35.1...v2.36.0) + +### Features + +* **api:** manual updates ([13c639c](https://github.com/openai/openai-python/commit/13c639cc7d57e4fbd4406563511e15eeb88a54b2)) +* **api:** realtime 2 ([8fe0ab8](https://github.com/openai/openai-python/commit/8fe0ab87e67eeb3cc27426b50093845229520f0e)) + ## 2.35.1 (2026-05-06) Full Changelog: [v2.35.0...v2.35.1](https://github.com/openai/openai-python/compare/v2.35.0...v2.35.1) diff --git a/pyproject.toml b/pyproject.toml index cb8c01191d..ec1af48c8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.35.1" +version = "2.36.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7f151cf0cd..a6435eede3 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.35.1" # x-release-please-version +__version__ = "2.36.0" # x-release-please-version From c85ebd935cb4b80e7e97ce255437684f6411fb00 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 16:32:52 +0000 Subject: [PATCH 356/408] fix(client): add missing f-string prefix in file type error message --- src/openai/_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openai/_files.py b/src/openai/_files.py index 4cc4f35d8f..1a2cc77478 100644 --- a/src/openai/_files.py +++ b/src/openai/_files.py @@ -99,7 +99,7 @@ async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles elif is_sequence_t(files): files = [(key, await _async_transform_file(file)) for key, file in files] else: - raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") return files From b279c591de38014c284ca33920f2cf22ac9a616f Mon Sep 17 00:00:00 2001 From: Romain Huet Date: Fri, 8 May 2026 23:28:51 -0700 Subject: [PATCH 357/408] Update README models to gpt-5.5 and gpt-realtime-2 --- README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9450c0bc51..ea48b247dd 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ client = OpenAI( ) response = client.responses.create( - model="gpt-5.2", + model="gpt-5.5", instructions="You are a coding assistant that talks like a pirate.", input="How do I check if a Python object is an instance of a class?", ) @@ -52,7 +52,7 @@ from openai import OpenAI client = OpenAI() completion = client.chat.completions.create( - model="gpt-5.2", + model="gpt-5.5", messages=[ {"role": "developer", "content": "Talk like a pirate."}, { @@ -95,7 +95,7 @@ client = OpenAI( ) response = client.chat.completions.create( - model="gpt-4", + model="gpt-5.5", messages=[{"role": "user", "content": "Hello!"}], ) ``` @@ -183,7 +183,7 @@ prompt = "What is in this image?" img_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg" response = client.responses.create( - model="gpt-5.2", + model="gpt-5.5", input=[ { "role": "user", @@ -209,7 +209,7 @@ with open("path/to/image.png", "rb") as image_file: b64_image = base64.b64encode(image_file.read()).decode("utf-8") response = client.responses.create( - model="gpt-5.2", + model="gpt-5.5", input=[ { "role": "user", @@ -239,7 +239,7 @@ client = AsyncOpenAI( async def main() -> None: response = await client.responses.create( - model="gpt-5.2", input="Explain disestablishmentarianism to a smart five year old." + model="gpt-5.5", input="Explain disestablishmentarianism to a smart five year old." ) print(response.output_text) @@ -281,7 +281,7 @@ async def main() -> None: "content": "Say this is a test", } ], - model="gpt-5.2", + model="gpt-5.5", ) @@ -298,7 +298,7 @@ from openai import OpenAI client = OpenAI() stream = client.responses.create( - model="gpt-5.2", + model="gpt-5.5", input="Write a one-sentence bedtime story about a unicorn.", stream=True, ) @@ -318,7 +318,7 @@ client = AsyncOpenAI() async def main(): stream = await client.responses.create( - model="gpt-5.2", + model="gpt-5.5", input="Write a one-sentence bedtime story about a unicorn.", stream=True, ) @@ -347,7 +347,7 @@ from openai import AsyncOpenAI async def main(): client = AsyncOpenAI() - async with client.realtime.connect(model="gpt-realtime") as connection: + async with client.realtime.connect(model="gpt-realtime-2") as connection: await connection.session.update( session={"type": "realtime", "output_modalities": ["text"]} ) @@ -383,7 +383,7 @@ Whenever an error occurs, the Realtime API will send an [`error` event](https:// ```py client = AsyncOpenAI() -async with client.realtime.connect(model="gpt-realtime") as connection: +async with client.realtime.connect(model="gpt-realtime-2") as connection: ... async for event in connection: if event.type == 'error': @@ -482,15 +482,15 @@ from openai import OpenAI client = OpenAI() -response = client.chat.responses.create( +response = client.responses.create( input=[ { "role": "user", "content": "How much ?", } ], - model="gpt-5.2", - response_format={"type": "json_object"}, + model="gpt-5.5", + text={"format": {"type": "json_object"}}, ) ``` @@ -644,7 +644,7 @@ All object responses in the SDK provide a `_request_id` property which is added ```python response = await client.responses.create( - model="gpt-5.2", + model="gpt-5.5", input="Say 'this is a test'.", ) print(response._request_id) # req_123 @@ -662,7 +662,7 @@ import openai try: completion = await client.chat.completions.create( - messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-5.2" + messages=[{"role": "user", "content": "Say this is a test"}], model="gpt-5.5" ) except openai.APIStatusError as exc: print(exc.request_id) # req_123 @@ -694,7 +694,7 @@ client.with_options(max_retries=5).chat.completions.create( "content": "How can I get the name of the current day in JavaScript?", } ], - model="gpt-5.2", + model="gpt-5.5", ) ``` @@ -725,7 +725,7 @@ client.with_options(timeout=5.0).chat.completions.create( "content": "How can I list all files in a directory using Python?", } ], - model="gpt-5.2", + model="gpt-5.5", ) ``` @@ -772,7 +772,7 @@ response = client.chat.completions.with_raw_response.create( "role": "user", "content": "Say this is a test", }], - model="gpt-5.2", + model="gpt-5.5", ) print(response.headers.get('X-My-Header')) @@ -805,7 +805,7 @@ with client.chat.completions.with_streaming_response.create( "content": "Say this is a test", } ], - model="gpt-5.2", + model="gpt-5.5", ) as response: print(response.headers.get("X-My-Header")) From 625827c5509ece3c40e5002be37a9bd9d91b5374 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 14:32:02 +0000 Subject: [PATCH 358/408] feat(api): add service_tier parameter to responses compact method --- .stats.yml | 4 ++-- src/openai/resources/responses/responses.py | 8 ++++++++ src/openai/types/responses/response_compact_params.py | 3 +++ tests/api_resources/test_responses.py | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9b6dc7e58b..38a36fd922 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-371f497afe4d6070f6e252e5febbe8f453c7058a8dff0c26a01b4d88442a4ac2.yml -openapi_spec_hash: d39f46e8fda45f77096448105efd175a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-50d816559ef0935e64d07789ff936a2b762e26ab0714a2fa6bc06d06d4484294.yml +openapi_spec_hash: c5d8f37edbf66c1fef627d787b4c54fd config_hash: b64135fff1fe9cf4069b9ecf59ae8b07 diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 4b8bd9af21..e83f9824be 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1704,6 +1704,7 @@ def compact( previous_response_id: Optional[str] | Omit = omit, prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1743,6 +1744,8 @@ def compact( prompt_cache_retention: How long to retain a prompt cache entry created by this request. + service_tier: The service tier to use for this request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1761,6 +1764,7 @@ def compact( "previous_response_id": previous_response_id, "prompt_cache_key": prompt_cache_key, "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, }, response_compact_params.ResponseCompactParams, ), @@ -3410,6 +3414,7 @@ async def compact( previous_response_id: Optional[str] | Omit = omit, prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3449,6 +3454,8 @@ async def compact( prompt_cache_retention: How long to retain a prompt cache entry created by this request. + service_tier: The service tier to use for this request. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -3467,6 +3474,7 @@ async def compact( "previous_response_id": previous_response_id, "prompt_cache_key": prompt_cache_key, "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, }, response_compact_params.ResponseCompactParams, ), diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index 2575438b34..923a09e56d 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -143,3 +143,6 @@ class ResponseCompactParams(TypedDict, total=False): prompt_cache_retention: Optional[Literal["in_memory", "24h"]] """How long to retain a prompt cache entry created by this request.""" + + service_tier: Optional[Literal["auto", "default", "flex", "priority"]] + """The service tier to use for this request.""" diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 40405f61b2..094687c2c6 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -389,6 +389,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None: previous_response_id="resp_123", prompt_cache_key="prompt_cache_key", prompt_cache_retention="in_memory", + service_tier="auto", ) assert_matches_type(CompactedResponse, response, path=["response"]) @@ -801,6 +802,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) - previous_response_id="resp_123", prompt_cache_key="prompt_cache_key", prompt_cache_retention="in_memory", + service_tier="auto", ) assert_matches_type(CompactedResponse, response, path=["response"]) From 7e527bc927cc58b74d7619abf7f1fbcfff8bddfa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 17:38:40 +0000 Subject: [PATCH 359/408] feat(internal/types): support eagerly validating pydantic iterators --- src/openai/_models.py | 80 +++++++++++++++++++++++++++++++++++++++++++ tests/test_models.py | 60 ++++++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/src/openai/_models.py b/src/openai/_models.py index 5f12232437..ed4c1f82d6 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -27,7 +27,9 @@ Protocol, Required, Sequence, + Annotated, ParamSpec, + TypeAlias, TypedDict, TypeGuard, final, @@ -81,7 +83,15 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: + from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler + from pydantic_core import CoreSchema, core_schema from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema +else: + try: + from pydantic_core import CoreSchema, core_schema + except ImportError: + CoreSchema = None + core_schema = None __all__ = ["BaseModel", "GenericModel"] @@ -422,6 +432,76 @@ def model_dump_json( ) +class _EagerIterable(list[_T], Generic[_T]): + """ + Accepts any Iterable[T] input (including generators), consumes it + eagerly, and validates all items upfront. + + Validation preserves the original container type where possible + (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) + always emits a list — round-tripping through model_dump() will not + restore the original container type. + """ + + @classmethod + def __get_pydantic_core_schema__( + cls, + source_type: Any, + handler: GetCoreSchemaHandler, + ) -> CoreSchema: + (item_type,) = get_args(source_type) or (Any,) + item_schema: CoreSchema = handler.generate_schema(item_type) + list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_of_items_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + cls._serialize, + info_arg=False, + ), + ) + + @staticmethod + def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: + original_type: type[Any] = type(v) + + # Normalize to list so list_schema can validate each item + if isinstance(v, list): + items: list[_T] = v + else: + try: + items = list(v) + except TypeError as e: + raise TypeError("Value is not iterable") from e + + # Validate items against the inner schema + validated: list[_T] = handler(items) + + # Reconstruct original container type + if original_type is list: + return validated + # str(list) produces the list's repr, not a string built from items, + # so skip reconstruction for str and its subclasses. + if issubclass(original_type, str): + return validated + try: + return original_type(validated) + except (TypeError, ValueError): + # If the type cannot be reconstructed, just return the validated list + return validated + + @staticmethod + def _serialize(v: Iterable[_T]) -> list[_T]: + """Always serialize as a list so Pydantic's JSON encoder is happy.""" + if isinstance(v, list): + return v + return list(v) + + +EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] + + def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) diff --git a/tests/test_models.py b/tests/test_models.py index 588869ee35..cc204bac1d 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,8 @@ import json -from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Iterable, Optional, cast from datetime import datetime, timezone -from typing_extensions import Literal, Annotated, TypeAliasType +from collections import deque +from typing_extensions import Literal, Annotated, TypedDict, TypeAliasType import pytest import pydantic @@ -9,7 +10,7 @@ from openai._utils import PropertyInfo from openai._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from openai._models import DISCRIMINATOR_CACHE, BaseModel, construct_type +from openai._models import DISCRIMINATOR_CACHE, BaseModel, EagerIterable, construct_type class BasicModel(BaseModel): @@ -961,3 +962,56 @@ def __getattr__(self, attr: str) -> Item: ... assert model.a.prop == 1 assert isinstance(model.a, Item) assert model.other == "foo" + + +# NOTE: Workaround for Pydantic Iterable behavior. +# Iterable fields are replaced with a ValidatorIterator and may be consumed +# during serialization, which can cause subsequent dumps to return empty data. +# See: https://github.com/pydantic/pydantic/issues/9541 +@pytest.mark.parametrize( + "data, expected_validated", + [ + ([1, 2, 3], [1, 2, 3]), + ((1, 2, 3), (1, 2, 3)), + (set([1, 2, 3]), set([1, 2, 3])), + (iter([1, 2, 3]), [1, 2, 3]), + ([], []), + ((x for x in [1, 2, 3]), [1, 2, 3]), + (map(lambda x: x, [1, 2, 3]), [1, 2, 3]), + (frozenset([1, 2, 3]), frozenset([1, 2, 3])), + (deque([1, 2, 3]), deque([1, 2, 3])), + ], + ids=["list", "tuple", "set", "iterator", "empty", "generator", "map", "frozenset", "deque"], +) +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction(data: Iterable[int], expected_validated: Iterable[int]) -> None: + class TypeWithIterable(TypedDict): + items: EagerIterable[int] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": data}}) + assert m.data["items"] == expected_validated + + # Verify repeated dumps don't lose data (the original bug) + assert m.model_dump()["data"]["items"] == list(expected_validated) + assert m.model_dump()["data"]["items"] == list(expected_validated) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction_str_falls_back_to_list() -> None: + # str is iterable (over chars), but str(list_of_chars) produces the list's repr + # rather than reconstructing a string from items. We special-case str to fall + # back to list instead of attempting reconstruction. + class TypeWithIterable(TypedDict): + items: EagerIterable[str] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": "hello"}}) + + # falls back to list of chars rather than calling str(["h", "e", "l", "l", "o"]) + assert m.data["items"] == ["h", "e", "l", "l", "o"] + assert m.model_dump()["data"]["items"] == ["h", "e", "l", "l", "o"] From c39ea8d12a010052d7f02cebe8daabd2d1f89597 Mon Sep 17 00:00:00 2001 From: Jingjing Tian Date: Tue, 12 May 2026 12:17:28 -0700 Subject: [PATCH 360/408] feat: Remove unnecessary client_id when using workload identity provider for auth --- README.md | 7 ------- src/openai/auth/_workload.py | 5 ----- tests/test_auth.py | 4 ---- tests/test_client.py | 1 - 4 files changed, 17 deletions(-) diff --git a/README.md b/README.md index 9450c0bc51..90f1c2cbd9 100644 --- a/README.md +++ b/README.md @@ -83,15 +83,12 @@ from openai.auth import k8s_service_account_token_provider client = OpenAI( workload_identity={ - "client_id": "your-client-id", "identity_provider_id": "idp-123", "service_account_id": "sa-456", "provider": k8s_service_account_token_provider( "/var/run/secrets/kubernetes.io/serviceaccount/token" ), }, - organization="org-xyz", - project="proj-abc", ) response = client.chat.completions.create( @@ -108,7 +105,6 @@ from openai.auth import azure_managed_identity_token_provider client = OpenAI( workload_identity={ - "client_id": "your-client-id", "identity_provider_id": "idp-123", "service_account_id": "sa-456", "provider": azure_managed_identity_token_provider( @@ -126,7 +122,6 @@ from openai.auth import gcp_id_token_provider client = OpenAI( workload_identity={ - "client_id": "your-client-id", "identity_provider_id": "idp-123", "service_account_id": "sa-456", "provider": gcp_id_token_provider(audience="https://api.openai.com/v1"), @@ -146,7 +141,6 @@ def get_custom_token() -> str: client = OpenAI( workload_identity={ - "client_id": "your-client-id", "identity_provider_id": "idp-123", "service_account_id": "sa-456", "provider": { @@ -165,7 +159,6 @@ from openai.auth import k8s_service_account_token_provider client = OpenAI( workload_identity={ - "client_id": "your-client-id", "identity_provider_id": "idp-123", "service_account_id": "sa-456", "provider": k8s_service_account_token_provider("/var/token"), diff --git a/src/openai/auth/_workload.py b/src/openai/auth/_workload.py index e3f6f7fb75..6c142a82ed 100644 --- a/src/openai/auth/_workload.py +++ b/src/openai/auth/_workload.py @@ -27,10 +27,6 @@ class SubjectTokenProvider(TypedDict): class WorkloadIdentity(TypedDict): - """A unique string that identifies the client.""" - - client_id: str - """Identity provider resource id in WIFAPI.""" identity_provider_id: str @@ -253,7 +249,6 @@ def _fetch_token_from_exchange(self) -> dict[str, Any]: self.token_exchange_url, json={ "grant_type": TOKEN_EXCHANGE_GRANT_TYPE, - "client_id": self.workload_identity["client_id"], "subject_token": subject_token, "subject_token_type": subject_token_type, "identity_provider_id": self.workload_identity["identity_provider_id"], diff --git a/tests/test_auth.py b/tests/test_auth.py index c8f4f2d7cf..17e9cd814c 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -36,7 +36,6 @@ def test_basic_auth(): client = OpenAI( workload_identity={ - "client_id": "client_123", "identity_provider_id": "idp_123", "service_account_id": "sa_123", "provider": { @@ -82,7 +81,6 @@ def provider() -> str: client = OpenAI( workload_identity={ - "client_id": "client_123", "identity_provider_id": "idp_123", "service_account_id": "sa_123", "provider": { @@ -103,7 +101,6 @@ def provider() -> str: assert json.loads(exchange_request.content) == snapshot( { "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", - "client_id": "client_123", "subject_token": "fake_subject_token", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "identity_provider_id": "idp_123", @@ -136,7 +133,6 @@ def test_workload_identity_exchange_error() -> None: client = OpenAI( workload_identity={ - "client_id": "client_123", "identity_provider_id": "idp_123", "service_account_id": "sa_123", "provider": { diff --git a/tests/test_client.py b/tests/test_client.py index e2bb6ea966..2d8955a58e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -45,7 +45,6 @@ api_key = "My API Key" admin_api_key = "My Admin API Key" workload_identity: WorkloadIdentity = { - "client_id": "client_123", "identity_provider_id": "provider_123", "service_account_id": "service_account_123", "provider": { From 8a7cac34cbc64fe02854beb3659f4bb5f46815f9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 15:55:14 +0000 Subject: [PATCH 361/408] release: 2.37.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 986390db98..28c811f943 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.36.0" + ".": "2.37.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index af067330f6..803d3d3a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 2.37.0 (2026-05-13) + +Full Changelog: [v2.36.0...v2.37.0](https://github.com/openai/openai-python/compare/v2.36.0...v2.37.0) + +### Features + +* **api:** add service_tier parameter to responses compact method ([625827c](https://github.com/openai/openai-python/commit/625827c5509ece3c40e5002be37a9bd9d91b5374)) +* **internal/types:** support eagerly validating pydantic iterators ([7e527bc](https://github.com/openai/openai-python/commit/7e527bc927cc58b74d7619abf7f1fbcfff8bddfa)) +* Remove unnecessary client_id when using workload identity provider for auth ([c39ea8d](https://github.com/openai/openai-python/commit/c39ea8d12a010052d7f02cebe8daabd2d1f89597)) + + +### Bug Fixes + +* **client:** add missing f-string prefix in file type error message ([c85ebd9](https://github.com/openai/openai-python/commit/c85ebd935cb4b80e7e97ce255437684f6411fb00)) + ## 2.36.0 (2026-05-07) Full Changelog: [v2.35.1...v2.36.0](https://github.com/openai/openai-python/compare/v2.35.1...v2.36.0) diff --git a/pyproject.toml b/pyproject.toml index ec1af48c8b..452ac3125a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.36.0" +version = "2.37.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a6435eede3..43d6a12d19 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.36.0" # x-release-please-version +__version__ = "2.37.0" # x-release-please-version From c336bcb78c39f2d35d62f06014d096051e2688af Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 04:09:10 +0000 Subject: [PATCH 362/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 38a36fd922..4bfc577213 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-50d816559ef0935e64d07789ff936a2b762e26ab0714a2fa6bc06d06d4484294.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-7f30f2390f57603d13ed862d360185cfb4f48b4bf9e56e73d1d17ae7e3dfc423.yml openapi_spec_hash: c5d8f37edbf66c1fef627d787b4c54fd -config_hash: b64135fff1fe9cf4069b9ecf59ae8b07 +config_hash: b496fe9c594a1820ad169a6f2d841a32 From a2f1d6c56980713619760c60a5c7bfb580b0adcb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 17:26:19 +0000 Subject: [PATCH 363/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4bfc577213..a3e07379dd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-7f30f2390f57603d13ed862d360185cfb4f48b4bf9e56e73d1d17ae7e3dfc423.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0127d9dfdff671141b07eb8fda98f6297f70267d2fa9249f4b0defb6826e5aba.yml openapi_spec_hash: c5d8f37edbf66c1fef627d787b4c54fd -config_hash: b496fe9c594a1820ad169a6f2d841a32 +config_hash: 70ff4da62a6a6ef9f91d566c38b7bddf From 2897485d445f2924c5c2a8e6a9f40eec633ff345 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 23:12:26 +0000 Subject: [PATCH 364/408] feat(api): update OpenAPI spec or Stainless config --- .stats.yml | 4 +- .../resources/admin/organization/invites.py | 6 +- .../organization/invite_create_params.py | 3 +- .../usage_audio_speeches_response.py | 79 +++++++++++++++++++ .../usage_audio_transcriptions_response.py | 79 +++++++++++++++++++ ...sage_code_interpreter_sessions_response.py | 79 +++++++++++++++++++ .../usage_completions_response.py | 79 +++++++++++++++++++ .../organization/usage_costs_response.py | 79 +++++++++++++++++++ .../organization/usage_embeddings_response.py | 79 +++++++++++++++++++ .../organization/usage_images_response.py | 79 +++++++++++++++++++ .../usage_moderations_response.py | 79 +++++++++++++++++++ .../usage_vector_stores_response.py | 79 +++++++++++++++++++ .../types/responses/response_input_item.py | 9 +++ .../responses/response_input_item_param.py | 9 +++ .../types/responses/response_input_param.py | 9 +++ 15 files changed, 746 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index a3e07379dd..f805dd3c95 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0127d9dfdff671141b07eb8fda98f6297f70267d2fa9249f4b0defb6826e5aba.yml -openapi_spec_hash: c5d8f37edbf66c1fef627d787b4c54fd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-fcfdf0bb920ca15a6d01d1cfe988accb7559b2d991b6e2ad6b25cfae81ac2ae9.yml +openapi_spec_hash: 72ee7d4fe582e75d16e67b6c97881fed config_hash: 70ff4da62a6a6ef9f91d566c38b7bddf diff --git a/src/openai/resources/admin/organization/invites.py b/src/openai/resources/admin/organization/invites.py index 23b83ccb18..b9db8c622c 100644 --- a/src/openai/resources/admin/organization/invites.py +++ b/src/openai/resources/admin/organization/invites.py @@ -67,7 +67,8 @@ def create( projects: An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project - for compatibility with legacy behavior. + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. extra_headers: Send extra headers @@ -270,7 +271,8 @@ async def create( projects: An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project - for compatibility with legacy behavior. + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. extra_headers: Send extra headers diff --git a/src/openai/types/admin/organization/invite_create_params.py b/src/openai/types/admin/organization/invite_create_params.py index 7709003fe3..51430d8694 100644 --- a/src/openai/types/admin/organization/invite_create_params.py +++ b/src/openai/types/admin/organization/invite_create_params.py @@ -19,7 +19,8 @@ class InviteCreateParams(TypedDict, total=False): """ An array of projects to which membership is granted at the same time the org invite is accepted. If omitted, the user will be invited to the default project - for compatibility with legacy behavior. + for compatibility with legacy behavior. If empty list is passed, the user will + not be invited to any projects, including the default one. """ diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py index e5fed36b03..f99a73eeb0 100644 --- a/src/openai/types/admin/organization/usage_audio_speeches_response.py +++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py index c5b77f34e1..d8f436b0b5 100644 --- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py index aafda6f113..0c92f61446 100644 --- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py +++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py index f179149995..57f0b699ec 100644 --- a/src/openai/types/admin/organization/usage_completions_response.py +++ b/src/openai/types/admin/organization/usage_completions_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py index fd1d344e26..71eee1188a 100644 --- a/src/openai/types/admin/organization/usage_costs_response.py +++ b/src/openai/types/admin/organization/usage_costs_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py index adbc389d89..69697ee4e7 100644 --- a/src/openai/types/admin/organization/usage_embeddings_response.py +++ b/src/openai/types/admin/organization/usage_embeddings_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py index 7c6a096c98..33c3ebb130 100644 --- a/src/openai/types/admin/organization/usage_images_response.py +++ b/src/openai/types/admin/organization/usage_images_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py index 5e40ef1164..3aa5d9193d 100644 --- a/src/openai/types/admin/organization/usage_moderations_response.py +++ b/src/openai/types/admin/organization/usage_moderations_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py index 089aa23119..a5ecc31185 100644 --- a/src/openai/types/admin/organization/usage_vector_stores_response.py +++ b/src/openai/types/admin/organization/usage_vector_stores_response.py @@ -18,6 +18,8 @@ "DataResultOrganizationUsageAudioTranscriptionsResult", "DataResultOrganizationUsageVectorStoresResult", "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", "DataResultOrganizationCostsResult", "DataResultOrganizationCostsResultAmount", ] @@ -317,6 +319,81 @@ class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): """ +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + class DataResultOrganizationCostsResultAmount(BaseModel): """The monetary value in its associated currency.""" @@ -370,6 +447,8 @@ class DataResultOrganizationCostsResult(BaseModel): DataResultOrganizationUsageAudioTranscriptionsResult, DataResultOrganizationUsageVectorStoresResult, DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, DataResultOrganizationCostsResult, ], PropertyInfo(discriminator="object"), diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index af3ce5bdc9..9f12b429bd 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -50,6 +50,7 @@ "McpApprovalRequest", "McpApprovalResponse", "McpCall", + "CompactionTrigger", "ItemReference", ] @@ -527,6 +528,13 @@ class McpCall(BaseModel): """ +class CompactionTrigger(BaseModel): + """Compacts the current context. Must be the final input item.""" + + type: Literal["compaction_trigger"] + """The type of the item. Always `compaction_trigger`.""" + + class ItemReference(BaseModel): """An internal identifier for an item to reference.""" @@ -566,6 +574,7 @@ class ItemReference(BaseModel): McpCall, ResponseCustomToolCallOutput, ResponseCustomToolCall, + CompactionTrigger, ItemReference, ], PropertyInfo(discriminator="type"), diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 87ea1bc572..156ac92d39 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -51,6 +51,7 @@ "McpApprovalRequest", "McpApprovalResponse", "McpCall", + "CompactionTrigger", "ItemReference", ] @@ -525,6 +526,13 @@ class McpCall(TypedDict, total=False): """ +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + class ItemReference(TypedDict, total=False): """An internal identifier for an item to reference.""" @@ -563,5 +571,6 @@ class ItemReference(TypedDict, total=False): McpCall, ResponseCustomToolCallOutputParam, ResponseCustomToolCallParam, + CompactionTrigger, ItemReference, ] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index cf4d529521..656c70359b 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -52,6 +52,7 @@ "McpApprovalRequest", "McpApprovalResponse", "McpCall", + "CompactionTrigger", "ItemReference", ] @@ -526,6 +527,13 @@ class McpCall(TypedDict, total=False): """ +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + class ItemReference(TypedDict, total=False): """An internal identifier for an item to reference.""" @@ -564,6 +572,7 @@ class ItemReference(TypedDict, total=False): McpCall, ResponseCustomToolCallOutputParam, ResponseCustomToolCallParam, + CompactionTrigger, ItemReference, ] From a6f899aa1e046dd0cc18b89c4f73260463888db6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 18:55:17 +0000 Subject: [PATCH 365/408] feat(api): manual updates Update specs --- .stats.yml | 6 +- api.md | 34 ++ .../admin/organization/projects/__init__.py | 28 ++ .../projects/hosted_tool_permissions.py | 307 +++++++++++ .../projects/model_permissions.py | 370 ++++++++++++++ .../admin/organization/projects/projects.py | 64 +++ .../resources/admin/organization/usage.py | 384 ++++++++++++++ .../types/admin/organization/__init__.py | 4 + .../admin/organization/projects/__init__.py | 5 + .../hosted_tool_permission_update_params.py | 60 +++ .../model_permission_update_params.py | 17 + .../project_hosted_tool_permissions.py | 59 +++ .../projects/project_model_permissions.py | 23 + .../project_model_permissions_deleted.py | 17 + .../usage_file_search_calls_params.py | 57 +++ .../usage_file_search_calls_response.py | 475 ++++++++++++++++++ .../usage_web_search_calls_params.py | 60 +++ .../usage_web_search_calls_response.py | 475 ++++++++++++++++++ .../projects/test_hosted_tool_permissions.py | 200 ++++++++ .../projects/test_model_permissions.py | 271 ++++++++++ .../admin/organization/test_usage.py | 192 +++++++ 21 files changed, 3105 insertions(+), 3 deletions(-) create mode 100644 src/openai/resources/admin/organization/projects/hosted_tool_permissions.py create mode 100644 src/openai/resources/admin/organization/projects/model_permissions.py create mode 100644 src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py create mode 100644 src/openai/types/admin/organization/projects/model_permission_update_params.py create mode 100644 src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py create mode 100644 src/openai/types/admin/organization/projects/project_model_permissions.py create mode 100644 src/openai/types/admin/organization/projects/project_model_permissions_deleted.py create mode 100644 src/openai/types/admin/organization/usage_file_search_calls_params.py create mode 100644 src/openai/types/admin/organization/usage_file_search_calls_response.py create mode 100644 src/openai/types/admin/organization/usage_web_search_calls_params.py create mode 100644 src/openai/types/admin/organization/usage_web_search_calls_response.py create mode 100644 tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py create mode 100644 tests/api_resources/admin/organization/projects/test_model_permissions.py diff --git a/.stats.yml b/.stats.yml index f805dd3c95..60770c0e7a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 233 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-fcfdf0bb920ca15a6d01d1cfe988accb7559b2d991b6e2ad6b25cfae81ac2ae9.yml +configured_endpoints: 240 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-d7d5adb20c516e7aca6e35ed9f58aba0bc14dd5aa0096fd980061c4a5ab406e3.yml openapi_spec_hash: 72ee7d4fe582e75d16e67b6c97881fed -config_hash: 70ff4da62a6a6ef9f91d566c38b7bddf +config_hash: af72288617facaeed4d014024a9c946d diff --git a/api.md b/api.md index e04636937c..ab2f3f75a2 100644 --- a/api.md +++ b/api.md @@ -754,9 +754,11 @@ from openai.types.admin.organization import ( UsageCompletionsResponse, UsageCostsResponse, UsageEmbeddingsResponse, + UsageFileSearchCallsResponse, UsageImagesResponse, UsageModerationsResponse, UsageVectorStoresResponse, + UsageWebSearchCallsResponse, ) ``` @@ -768,9 +770,11 @@ Methods: - client.admin.organization.usage.completions(\*\*params) -> UsageCompletionsResponse - client.admin.organization.usage.costs(\*\*params) -> UsageCostsResponse - client.admin.organization.usage.embeddings(\*\*params) -> UsageEmbeddingsResponse +- client.admin.organization.usage.file_search_calls(\*\*params) -> UsageFileSearchCallsResponse - client.admin.organization.usage.images(\*\*params) -> UsageImagesResponse - client.admin.organization.usage.moderations(\*\*params) -> UsageModerationsResponse - client.admin.organization.usage.vector_stores(\*\*params) -> UsageVectorStoresResponse +- client.admin.organization.usage.web_search_calls(\*\*params) -> UsageWebSearchCallsResponse ### Invites @@ -1006,6 +1010,36 @@ Methods: - client.admin.organization.projects.rate_limits.list_rate_limits(project_id, \*\*params) -> SyncConversationCursorPage[ProjectRateLimit] - client.admin.organization.projects.rate_limits.update_rate_limit(rate_limit_id, \*, project_id, \*\*params) -> ProjectRateLimit +#### ModelPermissions + +Types: + +```python +from openai.types.admin.organization.projects import ( + ProjectModelPermissions, + ProjectModelPermissionsDeleted, +) +``` + +Methods: + +- client.admin.organization.projects.model_permissions.retrieve(project_id) -> ProjectModelPermissions +- client.admin.organization.projects.model_permissions.update(project_id, \*\*params) -> ProjectModelPermissions +- client.admin.organization.projects.model_permissions.delete(project_id) -> ProjectModelPermissionsDeleted + +#### HostedToolPermissions + +Types: + +```python +from openai.types.admin.organization.projects import ProjectHostedToolPermissions +``` + +Methods: + +- client.admin.organization.projects.hosted_tool_permissions.retrieve(project_id) -> ProjectHostedToolPermissions +- client.admin.organization.projects.hosted_tool_permissions.update(project_id, \*\*params) -> ProjectHostedToolPermissions + #### Groups Types: diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py index a64326bfa9..7f77863a2d 100644 --- a/src/openai/resources/admin/organization/projects/__init__.py +++ b/src/openai/resources/admin/organization/projects/__init__.py @@ -64,6 +64,22 @@ ServiceAccountsWithStreamingResponse, AsyncServiceAccountsWithStreamingResponse, ) +from .model_permissions import ( + ModelPermissions, + AsyncModelPermissions, + ModelPermissionsWithRawResponse, + AsyncModelPermissionsWithRawResponse, + ModelPermissionsWithStreamingResponse, + AsyncModelPermissionsWithStreamingResponse, +) +from .hosted_tool_permissions import ( + HostedToolPermissions, + AsyncHostedToolPermissions, + HostedToolPermissionsWithRawResponse, + AsyncHostedToolPermissionsWithRawResponse, + HostedToolPermissionsWithStreamingResponse, + AsyncHostedToolPermissionsWithStreamingResponse, +) __all__ = [ "Users", @@ -90,6 +106,18 @@ "AsyncRateLimitsWithRawResponse", "RateLimitsWithStreamingResponse", "AsyncRateLimitsWithStreamingResponse", + "ModelPermissions", + "AsyncModelPermissions", + "ModelPermissionsWithRawResponse", + "AsyncModelPermissionsWithRawResponse", + "ModelPermissionsWithStreamingResponse", + "AsyncModelPermissionsWithStreamingResponse", + "HostedToolPermissions", + "AsyncHostedToolPermissions", + "HostedToolPermissionsWithRawResponse", + "AsyncHostedToolPermissionsWithRawResponse", + "HostedToolPermissionsWithStreamingResponse", + "AsyncHostedToolPermissionsWithStreamingResponse", "Groups", "AsyncGroups", "GroupsWithRawResponse", diff --git a/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py new file mode 100644 index 0000000000..27bcee5f5e --- /dev/null +++ b/src/openai/resources/admin/organization/projects/hosted_tool_permissions.py @@ -0,0 +1,307 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import hosted_tool_permission_update_params +from .....types.admin.organization.projects.project_hosted_tool_permissions import ProjectHostedToolPermissions + +__all__ = ["HostedToolPermissions", "AsyncHostedToolPermissions"] + + +class HostedToolPermissions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> HostedToolPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return HostedToolPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> HostedToolPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return HostedToolPermissionsWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Returns hosted tool permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + def update( + self, + project_id: str, + *, + code_interpreter: Optional[hosted_tool_permission_update_params.CodeInterpreter] | Omit = omit, + file_search: Optional[hosted_tool_permission_update_params.FileSearch] | Omit = omit, + image_generation: Optional[hosted_tool_permission_update_params.ImageGeneration] | Omit = omit, + mcp: Optional[hosted_tool_permission_update_params.Mcp] | Omit = omit, + web_search: Optional[hosted_tool_permission_update_params.WebSearch] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Updates hosted tool permissions for a project. + + Args: + code_interpreter: The code interpreter permission update. + + file_search: The file search permission update. + + image_generation: The image generation permission update. + + mcp: The MCP permission update. + + web_search: The web search permission update. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + body=maybe_transform( + { + "code_interpreter": code_interpreter, + "file_search": file_search, + "image_generation": image_generation, + "mcp": mcp, + "web_search": web_search, + }, + hosted_tool_permission_update_params.HostedToolPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + +class AsyncHostedToolPermissions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncHostedToolPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncHostedToolPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncHostedToolPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncHostedToolPermissionsWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Returns hosted tool permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + async def update( + self, + project_id: str, + *, + code_interpreter: Optional[hosted_tool_permission_update_params.CodeInterpreter] | Omit = omit, + file_search: Optional[hosted_tool_permission_update_params.FileSearch] | Omit = omit, + image_generation: Optional[hosted_tool_permission_update_params.ImageGeneration] | Omit = omit, + mcp: Optional[hosted_tool_permission_update_params.Mcp] | Omit = omit, + web_search: Optional[hosted_tool_permission_update_params.WebSearch] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectHostedToolPermissions: + """ + Updates hosted tool permissions for a project. + + Args: + code_interpreter: The code interpreter permission update. + + file_search: The file search permission update. + + image_generation: The image generation permission update. + + mcp: The MCP permission update. + + web_search: The web search permission update. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/hosted_tool_permissions", project_id=project_id), + body=await async_maybe_transform( + { + "code_interpreter": code_interpreter, + "file_search": file_search, + "image_generation": image_generation, + "mcp": mcp, + "web_search": web_search, + }, + hosted_tool_permission_update_params.HostedToolPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectHostedToolPermissions, + ) + + +class HostedToolPermissionsWithRawResponse: + def __init__(self, hosted_tool_permissions: HostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = _legacy_response.to_raw_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + hosted_tool_permissions.update, + ) + + +class AsyncHostedToolPermissionsWithRawResponse: + def __init__(self, hosted_tool_permissions: AsyncHostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + hosted_tool_permissions.update, + ) + + +class HostedToolPermissionsWithStreamingResponse: + def __init__(self, hosted_tool_permissions: HostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = to_streamed_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = to_streamed_response_wrapper( + hosted_tool_permissions.update, + ) + + +class AsyncHostedToolPermissionsWithStreamingResponse: + def __init__(self, hosted_tool_permissions: AsyncHostedToolPermissions) -> None: + self._hosted_tool_permissions = hosted_tool_permissions + + self.retrieve = async_to_streamed_response_wrapper( + hosted_tool_permissions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + hosted_tool_permissions.update, + ) diff --git a/src/openai/resources/admin/organization/projects/model_permissions.py b/src/openai/resources/admin/organization/projects/model_permissions.py new file mode 100644 index 0000000000..157b4a4f72 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/model_permissions.py @@ -0,0 +1,370 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, SequenceNotStr, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import model_permission_update_params +from .....types.admin.organization.projects.project_model_permissions import ProjectModelPermissions +from .....types.admin.organization.projects.project_model_permissions_deleted import ProjectModelPermissionsDeleted + +__all__ = ["ModelPermissions", "AsyncModelPermissions"] + + +class ModelPermissions(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ModelPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ModelPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ModelPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ModelPermissionsWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Returns model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + def update( + self, + project_id: str, + *, + mode: Literal["allow_list", "deny_list"], + model_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Updates model permissions for a project. + + Args: + mode: The model permissions mode to apply. + + model_ids: The model IDs included in this permissions policy. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + body=maybe_transform( + { + "mode": mode, + "model_ids": model_ids, + }, + model_permission_update_params.ModelPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissionsDeleted: + """ + Deletes model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._delete( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissionsDeleted, + ) + + +class AsyncModelPermissions(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncModelPermissionsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncModelPermissionsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncModelPermissionsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncModelPermissionsWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Returns model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + async def update( + self, + project_id: str, + *, + mode: Literal["allow_list", "deny_list"], + model_ids: SequenceNotStr[str], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissions: + """ + Updates model permissions for a project. + + Args: + mode: The model permissions mode to apply. + + model_ids: The model IDs included in this permissions policy. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + body=await async_maybe_transform( + { + "mode": mode, + "model_ids": model_ids, + }, + model_permission_update_params.ModelPermissionUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissions, + ) + + async def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectModelPermissionsDeleted: + """ + Deletes model permissions for a project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._delete( + path_template("/organization/projects/{project_id}/model_permissions", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectModelPermissionsDeleted, + ) + + +class ModelPermissionsWithRawResponse: + def __init__(self, model_permissions: ModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = _legacy_response.to_raw_response_wrapper( + model_permissions.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + model_permissions.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + model_permissions.delete, + ) + + +class AsyncModelPermissionsWithRawResponse: + def __init__(self, model_permissions: AsyncModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + model_permissions.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + model_permissions.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + model_permissions.delete, + ) + + +class ModelPermissionsWithStreamingResponse: + def __init__(self, model_permissions: ModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = to_streamed_response_wrapper( + model_permissions.retrieve, + ) + self.update = to_streamed_response_wrapper( + model_permissions.update, + ) + self.delete = to_streamed_response_wrapper( + model_permissions.delete, + ) + + +class AsyncModelPermissionsWithStreamingResponse: + def __init__(self, model_permissions: AsyncModelPermissions) -> None: + self._model_permissions = model_permissions + + self.retrieve = async_to_streamed_response_wrapper( + model_permissions.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + model_permissions.update, + ) + self.delete = async_to_streamed_response_wrapper( + model_permissions.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py index 60d252cd46..b69e69d0dd 100644 --- a/src/openai/resources/admin/organization/projects/projects.py +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -70,6 +70,22 @@ ServiceAccountsWithStreamingResponse, AsyncServiceAccountsWithStreamingResponse, ) +from .model_permissions import ( + ModelPermissions, + AsyncModelPermissions, + ModelPermissionsWithRawResponse, + AsyncModelPermissionsWithRawResponse, + ModelPermissionsWithStreamingResponse, + AsyncModelPermissionsWithStreamingResponse, +) +from .hosted_tool_permissions import ( + HostedToolPermissions, + AsyncHostedToolPermissions, + HostedToolPermissionsWithRawResponse, + AsyncHostedToolPermissionsWithRawResponse, + HostedToolPermissionsWithStreamingResponse, + AsyncHostedToolPermissionsWithStreamingResponse, +) from .....types.admin.organization import project_list_params, project_create_params, project_update_params from .....types.admin.organization.project import Project @@ -93,6 +109,14 @@ def api_keys(self) -> APIKeys: def rate_limits(self) -> RateLimits: return RateLimits(self._client) + @cached_property + def model_permissions(self) -> ModelPermissions: + return ModelPermissions(self._client) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissions: + return HostedToolPermissions(self._client) + @cached_property def groups(self) -> Groups: return Groups(self._client) @@ -386,6 +410,14 @@ def api_keys(self) -> AsyncAPIKeys: def rate_limits(self) -> AsyncRateLimits: return AsyncRateLimits(self._client) + @cached_property + def model_permissions(self) -> AsyncModelPermissions: + return AsyncModelPermissions(self._client) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissions: + return AsyncHostedToolPermissions(self._client) + @cached_property def groups(self) -> AsyncGroups: return AsyncGroups(self._client) @@ -698,6 +730,14 @@ def api_keys(self) -> APIKeysWithRawResponse: def rate_limits(self) -> RateLimitsWithRawResponse: return RateLimitsWithRawResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> ModelPermissionsWithRawResponse: + return ModelPermissionsWithRawResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissionsWithRawResponse: + return HostedToolPermissionsWithRawResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> GroupsWithRawResponse: return GroupsWithRawResponse(self._projects.groups) @@ -747,6 +787,14 @@ def api_keys(self) -> AsyncAPIKeysWithRawResponse: def rate_limits(self) -> AsyncRateLimitsWithRawResponse: return AsyncRateLimitsWithRawResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> AsyncModelPermissionsWithRawResponse: + return AsyncModelPermissionsWithRawResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissionsWithRawResponse: + return AsyncHostedToolPermissionsWithRawResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> AsyncGroupsWithRawResponse: return AsyncGroupsWithRawResponse(self._projects.groups) @@ -796,6 +844,14 @@ def api_keys(self) -> APIKeysWithStreamingResponse: def rate_limits(self) -> RateLimitsWithStreamingResponse: return RateLimitsWithStreamingResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> ModelPermissionsWithStreamingResponse: + return ModelPermissionsWithStreamingResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> HostedToolPermissionsWithStreamingResponse: + return HostedToolPermissionsWithStreamingResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> GroupsWithStreamingResponse: return GroupsWithStreamingResponse(self._projects.groups) @@ -845,6 +901,14 @@ def api_keys(self) -> AsyncAPIKeysWithStreamingResponse: def rate_limits(self) -> AsyncRateLimitsWithStreamingResponse: return AsyncRateLimitsWithStreamingResponse(self._projects.rate_limits) + @cached_property + def model_permissions(self) -> AsyncModelPermissionsWithStreamingResponse: + return AsyncModelPermissionsWithStreamingResponse(self._projects.model_permissions) + + @cached_property + def hosted_tool_permissions(self) -> AsyncHostedToolPermissionsWithStreamingResponse: + return AsyncHostedToolPermissionsWithStreamingResponse(self._projects.hosted_tool_permissions) + @cached_property def groups(self) -> AsyncGroupsWithStreamingResponse: return AsyncGroupsWithStreamingResponse(self._projects.groups) diff --git a/src/openai/resources/admin/organization/usage.py b/src/openai/resources/admin/organization/usage.py index 2725d5e884..99306c6828 100644 --- a/src/openai/resources/admin/organization/usage.py +++ b/src/openai/resources/admin/organization/usage.py @@ -22,6 +22,8 @@ usage_moderations_params, usage_vector_stores_params, usage_audio_speeches_params, + usage_web_search_calls_params, + usage_file_search_calls_params, usage_audio_transcriptions_params, usage_code_interpreter_sessions_params, ) @@ -32,6 +34,8 @@ from ....types.admin.organization.usage_moderations_response import UsageModerationsResponse from ....types.admin.organization.usage_vector_stores_response import UsageVectorStoresResponse from ....types.admin.organization.usage_audio_speeches_response import UsageAudioSpeechesResponse +from ....types.admin.organization.usage_web_search_calls_response import UsageWebSearchCallsResponse +from ....types.admin.organization.usage_file_search_calls_response import UsageFileSearchCallsResponse from ....types.admin.organization.usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse from ....types.admin.organization.usage_code_interpreter_sessions_response import UsageCodeInterpreterSessionsResponse @@ -557,6 +561,93 @@ def embeddings( cast_to=UsageEmbeddingsResponse, ) + def file_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + vector_store_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageFileSearchCallsResponse: + """ + Get file search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `vector_store_id` or any combination of + them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + vector_store_ids: Return only usage for these vector stores. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/file_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + "vector_store_ids": vector_store_ids, + }, + usage_file_search_calls_params.UsageFileSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageFileSearchCallsResponse, + ) + def images( self, *, @@ -814,6 +905,97 @@ def vector_stores( cast_to=UsageVectorStoresResponse, ) + def web_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + context_levels: List[Literal["low", "medium", "high"]] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageWebSearchCallsResponse: + """ + Get web search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + context_levels: Return only web search usage for these context levels. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `context_level` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/organization/usage/web_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "context_levels": context_levels, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_web_search_calls_params.UsageWebSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageWebSearchCallsResponse, + ) + class AsyncUsage(AsyncAPIResource): @cached_property @@ -1334,6 +1516,93 @@ async def embeddings( cast_to=UsageEmbeddingsResponse, ) + async def file_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] | Omit = omit, + limit: int | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + vector_store_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageFileSearchCallsResponse: + """ + Get file search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `vector_store_id` or any combination of + them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + vector_store_ids: Return only usage for these vector stores. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/file_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + "vector_store_ids": vector_store_ids, + }, + usage_file_search_calls_params.UsageFileSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageFileSearchCallsResponse, + ) + async def images( self, *, @@ -1591,6 +1860,97 @@ async def vector_stores( cast_to=UsageVectorStoresResponse, ) + async def web_search_calls( + self, + *, + start_time: int, + api_key_ids: SequenceNotStr[str] | Omit = omit, + bucket_width: Literal["1m", "1h", "1d"] | Omit = omit, + context_levels: List[Literal["low", "medium", "high"]] | Omit = omit, + end_time: int | Omit = omit, + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] | Omit = omit, + limit: int | Omit = omit, + models: SequenceNotStr[str] | Omit = omit, + page: str | Omit = omit, + project_ids: SequenceNotStr[str] | Omit = omit, + user_ids: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageWebSearchCallsResponse: + """ + Get web search calls usage details for the organization. + + Args: + start_time: Start time (Unix seconds) of the query time range, inclusive. + + api_key_ids: Return only usage for these API keys. + + bucket_width: Width of each time bucket in response. Currently `1m`, `1h` and `1d` are + supported, default to `1d`. + + context_levels: Return only web search usage for these context levels. + + end_time: End time (Unix seconds) of the query time range, exclusive. + + group_by: Group the usage data by the specified fields. Support fields include + `project_id`, `user_id`, `api_key_id`, `model`, `context_level` or any + combination of them. + + limit: Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + + models: Return only usage for these models. + + page: A cursor for use in pagination. Corresponding to the `next_page` field from the + previous response. + + project_ids: Return only usage for these projects. + + user_ids: Return only usage for these users. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/organization/usage/web_search_calls", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "start_time": start_time, + "api_key_ids": api_key_ids, + "bucket_width": bucket_width, + "context_levels": context_levels, + "end_time": end_time, + "group_by": group_by, + "limit": limit, + "models": models, + "page": page, + "project_ids": project_ids, + "user_ids": user_ids, + }, + usage_web_search_calls_params.UsageWebSearchCallsParams, + ), + security={"admin_api_key_auth": True}, + ), + cast_to=UsageWebSearchCallsResponse, + ) + class UsageWithRawResponse: def __init__(self, usage: Usage) -> None: @@ -1614,6 +1974,9 @@ def __init__(self, usage: Usage) -> None: self.embeddings = _legacy_response.to_raw_response_wrapper( usage.embeddings, ) + self.file_search_calls = _legacy_response.to_raw_response_wrapper( + usage.file_search_calls, + ) self.images = _legacy_response.to_raw_response_wrapper( usage.images, ) @@ -1623,6 +1986,9 @@ def __init__(self, usage: Usage) -> None: self.vector_stores = _legacy_response.to_raw_response_wrapper( usage.vector_stores, ) + self.web_search_calls = _legacy_response.to_raw_response_wrapper( + usage.web_search_calls, + ) class AsyncUsageWithRawResponse: @@ -1647,6 +2013,9 @@ def __init__(self, usage: AsyncUsage) -> None: self.embeddings = _legacy_response.async_to_raw_response_wrapper( usage.embeddings, ) + self.file_search_calls = _legacy_response.async_to_raw_response_wrapper( + usage.file_search_calls, + ) self.images = _legacy_response.async_to_raw_response_wrapper( usage.images, ) @@ -1656,6 +2025,9 @@ def __init__(self, usage: AsyncUsage) -> None: self.vector_stores = _legacy_response.async_to_raw_response_wrapper( usage.vector_stores, ) + self.web_search_calls = _legacy_response.async_to_raw_response_wrapper( + usage.web_search_calls, + ) class UsageWithStreamingResponse: @@ -1680,6 +2052,9 @@ def __init__(self, usage: Usage) -> None: self.embeddings = to_streamed_response_wrapper( usage.embeddings, ) + self.file_search_calls = to_streamed_response_wrapper( + usage.file_search_calls, + ) self.images = to_streamed_response_wrapper( usage.images, ) @@ -1689,6 +2064,9 @@ def __init__(self, usage: Usage) -> None: self.vector_stores = to_streamed_response_wrapper( usage.vector_stores, ) + self.web_search_calls = to_streamed_response_wrapper( + usage.web_search_calls, + ) class AsyncUsageWithStreamingResponse: @@ -1713,6 +2091,9 @@ def __init__(self, usage: AsyncUsage) -> None: self.embeddings = async_to_streamed_response_wrapper( usage.embeddings, ) + self.file_search_calls = async_to_streamed_response_wrapper( + usage.file_search_calls, + ) self.images = async_to_streamed_response_wrapper( usage.images, ) @@ -1722,3 +2103,6 @@ def __init__(self, usage: AsyncUsage) -> None: self.vector_stores = async_to_streamed_response_wrapper( usage.vector_stores, ) + self.web_search_calls = async_to_streamed_response_wrapper( + usage.web_search_calls, + ) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index dbb11795e5..ebb62b0eb8 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -56,7 +56,11 @@ from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse +from .usage_web_search_calls_params import UsageWebSearchCallsParams as UsageWebSearchCallsParams +from .usage_file_search_calls_params import UsageFileSearchCallsParams as UsageFileSearchCallsParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse +from .usage_web_search_calls_response import UsageWebSearchCallsResponse as UsageWebSearchCallsResponse +from .usage_file_search_calls_response import UsageFileSearchCallsResponse as UsageFileSearchCallsResponse from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse from .usage_code_interpreter_sessions_params import ( diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py index ea627ce7d6..1b78bae7a8 100644 --- a/src/openai/types/admin/organization/projects/__init__.py +++ b/src/openai/types/admin/organization/projects/__init__.py @@ -22,13 +22,18 @@ from .certificate_list_params import CertificateListParams as CertificateListParams from .project_service_account import ProjectServiceAccount as ProjectServiceAccount from .certificate_list_response import CertificateListResponse as CertificateListResponse +from .project_model_permissions import ProjectModelPermissions as ProjectModelPermissions from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams +from .model_permission_update_params import ModelPermissionUpdateParams as ModelPermissionUpdateParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse +from .project_hosted_tool_permissions import ProjectHostedToolPermissions as ProjectHostedToolPermissions from .service_account_create_response import ServiceAccountCreateResponse as ServiceAccountCreateResponse from .service_account_delete_response import ServiceAccountDeleteResponse as ServiceAccountDeleteResponse +from .project_model_permissions_deleted import ProjectModelPermissionsDeleted as ProjectModelPermissionsDeleted from .rate_limit_list_rate_limits_params import RateLimitListRateLimitsParams as RateLimitListRateLimitsParams from .rate_limit_update_rate_limit_params import RateLimitUpdateRateLimitParams as RateLimitUpdateRateLimitParams +from .hosted_tool_permission_update_params import HostedToolPermissionUpdateParams as HostedToolPermissionUpdateParams diff --git a/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py b/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py new file mode 100644 index 0000000000..e14f03741f --- /dev/null +++ b/src/openai/types/admin/organization/projects/hosted_tool_permission_update_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, TypedDict + +__all__ = ["HostedToolPermissionUpdateParams", "CodeInterpreter", "FileSearch", "ImageGeneration", "Mcp", "WebSearch"] + + +class HostedToolPermissionUpdateParams(TypedDict, total=False): + code_interpreter: Optional[CodeInterpreter] + """The code interpreter permission update.""" + + file_search: Optional[FileSearch] + """The file search permission update.""" + + image_generation: Optional[ImageGeneration] + """The image generation permission update.""" + + mcp: Optional[Mcp] + """The MCP permission update.""" + + web_search: Optional[WebSearch] + """The web search permission update.""" + + +class CodeInterpreter(TypedDict, total=False): + """The code interpreter permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class FileSearch(TypedDict, total=False): + """The file search permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class ImageGeneration(TypedDict, total=False): + """The image generation permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class Mcp(TypedDict, total=False): + """The MCP permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" + + +class WebSearch(TypedDict, total=False): + """The web search permission update.""" + + enabled: Required[bool] + """Whether to enable the hosted tool for the project.""" diff --git a/src/openai/types/admin/organization/projects/model_permission_update_params.py b/src/openai/types/admin/organization/projects/model_permission_update_params.py new file mode 100644 index 0000000000..bf7cb1d56c --- /dev/null +++ b/src/openai/types/admin/organization/projects/model_permission_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["ModelPermissionUpdateParams"] + + +class ModelPermissionUpdateParams(TypedDict, total=False): + mode: Required[Literal["allow_list", "deny_list"]] + """The model permissions mode to apply.""" + + model_ids: Required[SequenceNotStr[str]] + """The model IDs included in this permissions policy.""" diff --git a/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py b/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py new file mode 100644 index 0000000000..e89ed18af0 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_hosted_tool_permissions.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ....._models import BaseModel + +__all__ = ["ProjectHostedToolPermissions", "CodeInterpreter", "FileSearch", "ImageGeneration", "Mcp", "WebSearch"] + + +class CodeInterpreter(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class FileSearch(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class ImageGeneration(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class Mcp(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class WebSearch(BaseModel): + """Permission state for a single hosted tool on a project.""" + + enabled: bool + """Whether the hosted tool is enabled for the project.""" + + +class ProjectHostedToolPermissions(BaseModel): + """Represents hosted tool permissions for a project.""" + + code_interpreter: CodeInterpreter + """Permission state for a single hosted tool on a project.""" + + file_search: FileSearch + """Permission state for a single hosted tool on a project.""" + + image_generation: ImageGeneration + """Permission state for a single hosted tool on a project.""" + + mcp: Mcp + """Permission state for a single hosted tool on a project.""" + + web_search: WebSearch + """Permission state for a single hosted tool on a project.""" diff --git a/src/openai/types/admin/organization/projects/project_model_permissions.py b/src/openai/types/admin/organization/projects/project_model_permissions.py new file mode 100644 index 0000000000..597ae4e257 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_model_permissions.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ....._models import BaseModel + +__all__ = ["ProjectModelPermissions"] + + +class ProjectModelPermissions(BaseModel): + """Represents the model allowlist or denylist policy for a project.""" + + mode: Literal["allow_list", "deny_list"] + """Whether the project uses an allowlist or a denylist.""" + + api_model_ids: List[str] = FieldInfo(alias="model_ids") + """The model IDs included in the model permissions policy.""" + + object: Literal["project.model_permissions"] + """The object type, which is always `project.model_permissions`.""" diff --git a/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py b/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py new file mode 100644 index 0000000000..cb3972dce8 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_model_permissions_deleted.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectModelPermissionsDeleted"] + + +class ProjectModelPermissionsDeleted(BaseModel): + """Confirmation payload returned after deleting project model permissions.""" + + deleted: bool + """Whether the project model permissions were deleted.""" + + object: Literal["project.model_permissions.deleted"] + """The object type, which is always `project.model_permissions.deleted`.""" diff --git a/src/openai/types/admin/organization/usage_file_search_calls_params.py b/src/openai/types/admin/organization/usage_file_search_calls_params.py new file mode 100644 index 0000000000..914d568c33 --- /dev/null +++ b/src/openai/types/admin/organization/usage_file_search_calls_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageFileSearchCallsParams"] + + +class UsageFileSearchCallsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "vector_store_id"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `vector_store_id` + or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" + + vector_store_ids: SequenceNotStr[str] + """Return only usage for these vector stores.""" diff --git a/src/openai/types/admin/organization/usage_file_search_calls_response.py b/src/openai/types/admin/organization/usage_file_search_calls_response.py new file mode 100644 index 0000000000..0e093b8346 --- /dev/null +++ b/src/openai/types/admin/organization/usage_file_search_calls_response.py @@ -0,0 +1,475 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageFileSearchCallsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageFileSearchCallsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/src/openai/types/admin/organization/usage_web_search_calls_params.py b/src/openai/types/admin/organization/usage_web_search_calls_params.py new file mode 100644 index 0000000000..544060ade4 --- /dev/null +++ b/src/openai/types/admin/organization/usage_web_search_calls_params.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["UsageWebSearchCallsParams"] + + +class UsageWebSearchCallsParams(TypedDict, total=False): + start_time: Required[int] + """Start time (Unix seconds) of the query time range, inclusive.""" + + api_key_ids: SequenceNotStr[str] + """Return only usage for these API keys.""" + + bucket_width: Literal["1m", "1h", "1d"] + """Width of each time bucket in response. + + Currently `1m`, `1h` and `1d` are supported, default to `1d`. + """ + + context_levels: List[Literal["low", "medium", "high"]] + """Return only web search usage for these context levels.""" + + end_time: int + """End time (Unix seconds) of the query time range, exclusive.""" + + group_by: List[Literal["project_id", "user_id", "api_key_id", "model", "context_level"]] + """Group the usage data by the specified fields. + + Support fields include `project_id`, `user_id`, `api_key_id`, `model`, + `context_level` or any combination of them. + """ + + limit: int + """Specifies the number of buckets to return. + + - `bucket_width=1d`: default: 7, max: 31 + - `bucket_width=1h`: default: 24, max: 168 + - `bucket_width=1m`: default: 60, max: 1440 + """ + + models: SequenceNotStr[str] + """Return only usage for these models.""" + + page: str + """A cursor for use in pagination. + + Corresponding to the `next_page` field from the previous response. + """ + + project_ids: SequenceNotStr[str] + """Return only usage for these projects.""" + + user_ids: SequenceNotStr[str] + """Return only usage for these users.""" diff --git a/src/openai/types/admin/organization/usage_web_search_calls_response.py b/src/openai/types/admin/organization/usage_web_search_calls_response.py new file mode 100644 index 0000000000..b6fcd4ac6c --- /dev/null +++ b/src/openai/types/admin/organization/usage_web_search_calls_response.py @@ -0,0 +1,475 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ...._utils import PropertyInfo +from ...._models import BaseModel + +__all__ = [ + "UsageWebSearchCallsResponse", + "Data", + "DataResult", + "DataResultOrganizationUsageCompletionsResult", + "DataResultOrganizationUsageEmbeddingsResult", + "DataResultOrganizationUsageModerationsResult", + "DataResultOrganizationUsageImagesResult", + "DataResultOrganizationUsageAudioSpeechesResult", + "DataResultOrganizationUsageAudioTranscriptionsResult", + "DataResultOrganizationUsageVectorStoresResult", + "DataResultOrganizationUsageCodeInterpreterSessionsResult", + "DataResultOrganizationUsageFileSearchesResult", + "DataResultOrganizationUsageWebSearchesResult", + "DataResultOrganizationCostsResult", + "DataResultOrganizationCostsResultAmount", +] + + +class DataResultOrganizationUsageCompletionsResult(BaseModel): + """The aggregated completions usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of text input tokens used, including cached tokens. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.completions.result"] + + output_tokens: int + """The aggregated number of text output tokens used. + + For customers subscribe to scale tier, this includes scale tier tokens. + """ + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + batch: Optional[bool] = None + """ + When `group_by=batch`, this field tells whether the grouped usage result is + batch or not. + """ + + input_audio_tokens: Optional[int] = None + """The aggregated number of audio input tokens used, including cached tokens.""" + + input_cached_tokens: Optional[int] = None + """ + The aggregated number of text input tokens that has been cached from previous + requests. For customers subscribe to scale tier, this includes scale tier + tokens. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + output_audio_tokens: Optional[int] = None + """The aggregated number of audio output tokens used.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + service_tier: Optional[str] = None + """ + When `group_by=service_tier`, this field provides the service tier of the + grouped usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageEmbeddingsResult(BaseModel): + """The aggregated embeddings usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.embeddings.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageModerationsResult(BaseModel): + """The aggregated moderations usage details of the specific time bucket.""" + + input_tokens: int + """The aggregated number of input tokens used.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.moderations.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageImagesResult(BaseModel): + """The aggregated images usage details of the specific time bucket.""" + + images: int + """The number of images processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.images.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + size: Optional[str] = None + """ + When `group_by=size`, this field provides the image size of the grouped usage + result. + """ + + source: Optional[str] = None + """ + When `group_by=source`, this field provides the source of the grouped usage + result, possible values are `image.generation`, `image.edit`, `image.variation`. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioSpeechesResult(BaseModel): + """The aggregated audio speeches usage details of the specific time bucket.""" + + characters: int + """The number of characters processed.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_speeches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageAudioTranscriptionsResult(BaseModel): + """The aggregated audio transcriptions usage details of the specific time bucket.""" + + num_model_requests: int + """The count of requests made to the model.""" + + object: Literal["organization.usage.audio_transcriptions.result"] + + seconds: int + """The number of seconds processed.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationUsageVectorStoresResult(BaseModel): + """The aggregated vector stores usage details of the specific time bucket.""" + + object: Literal["organization.usage.vector_stores.result"] + + usage_bytes: int + """The vector stores usage in bytes.""" + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageCodeInterpreterSessionsResult(BaseModel): + """ + The aggregated code interpreter sessions usage details of the specific time bucket. + """ + + num_sessions: int + """The number of code interpreter sessions.""" + + object: Literal["organization.usage.code_interpreter_sessions.result"] + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + +class DataResultOrganizationUsageFileSearchesResult(BaseModel): + """The aggregated file search calls usage details of the specific time bucket.""" + + num_requests: int + """The count of file search calls.""" + + object: Literal["organization.usage.file_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + vector_store_id: Optional[str] = None + """ + When `group_by=vector_store_id`, this field provides the vector store ID of the + grouped usage result. + """ + + +class DataResultOrganizationUsageWebSearchesResult(BaseModel): + """The aggregated web search calls usage details of the specific time bucket.""" + + num_model_requests: int + """The count of model requests.""" + + num_requests: int + """The count of web search calls.""" + + object: Literal["organization.usage.web_searches.result"] + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API key ID of the grouped + usage result. + """ + + context_level: Optional[str] = None + """ + When `group_by=context_level`, this field provides the search context size of + the grouped usage result. + """ + + model: Optional[str] = None + """ + When `group_by=model`, this field provides the model name of the grouped usage + result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + usage result. + """ + + user_id: Optional[str] = None + """ + When `group_by=user_id`, this field provides the user ID of the grouped usage + result. + """ + + +class DataResultOrganizationCostsResultAmount(BaseModel): + """The monetary value in its associated currency.""" + + currency: Optional[str] = None + """Lowercase ISO-4217 currency e.g. "usd" """ + + value: Optional[float] = None + """The numeric value of the cost.""" + + +class DataResultOrganizationCostsResult(BaseModel): + """The aggregated costs details of the specific time bucket.""" + + object: Literal["organization.costs.result"] + + amount: Optional[DataResultOrganizationCostsResultAmount] = None + """The monetary value in its associated currency.""" + + api_key_id: Optional[str] = None + """ + When `group_by=api_key_id`, this field provides the API Key ID of the grouped + costs result. + """ + + line_item: Optional[str] = None + """ + When `group_by=line_item`, this field provides the line item of the grouped + costs result. + """ + + project_id: Optional[str] = None + """ + When `group_by=project_id`, this field provides the project ID of the grouped + costs result. + """ + + quantity: Optional[float] = None + """ + When `group_by=line_item`, this field provides the quantity of the grouped costs + result. + """ + + +DataResult: TypeAlias = Annotated[ + Union[ + DataResultOrganizationUsageCompletionsResult, + DataResultOrganizationUsageEmbeddingsResult, + DataResultOrganizationUsageModerationsResult, + DataResultOrganizationUsageImagesResult, + DataResultOrganizationUsageAudioSpeechesResult, + DataResultOrganizationUsageAudioTranscriptionsResult, + DataResultOrganizationUsageVectorStoresResult, + DataResultOrganizationUsageCodeInterpreterSessionsResult, + DataResultOrganizationUsageFileSearchesResult, + DataResultOrganizationUsageWebSearchesResult, + DataResultOrganizationCostsResult, + ], + PropertyInfo(discriminator="object"), +] + + +class Data(BaseModel): + end_time: int + + object: Literal["bucket"] + + results: List[DataResult] + + start_time: int + + +class UsageWebSearchCallsResponse(BaseModel): + data: List[Data] + + has_more: bool + + next_page: Optional[str] = None + + object: Literal["page"] diff --git a/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py b/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py new file mode 100644 index 0000000000..d7d2486874 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_hosted_tool_permissions.py @@ -0,0 +1,200 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects import ProjectHostedToolPermissions + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestHostedToolPermissions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + hosted_tool_permission = client.admin.organization.projects.hosted_tool_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + hosted_tool_permission = client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + hosted_tool_permission = client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + code_interpreter={"enabled": True}, + file_search={"enabled": True}, + image_generation={"enabled": True}, + mcp={"enabled": True}, + web_search={"enabled": True}, + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.update( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="", + ) + + +class TestAsyncHostedToolPermissions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + hosted_tool_permission = await async_client.admin.organization.projects.hosted_tool_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = await response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + hosted_tool_permission = await async_client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + hosted_tool_permission = await async_client.admin.organization.projects.hosted_tool_permissions.update( + project_id="project_id", + code_interpreter={"enabled": True}, + file_search={"enabled": True}, + image_generation={"enabled": True}, + mcp={"enabled": True}, + web_search={"enabled": True}, + ) + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + hosted_tool_permission = response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.hosted_tool_permissions.with_streaming_response.update( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + hosted_tool_permission = await response.parse() + assert_matches_type(ProjectHostedToolPermissions, hosted_tool_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.hosted_tool_permissions.with_raw_response.update( + project_id="", + ) diff --git a/tests/api_resources/admin/organization/projects/test_model_permissions.py b/tests/api_resources/admin/organization/projects/test_model_permissions.py new file mode 100644 index 0000000000..1749a70fa5 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_model_permissions.py @@ -0,0 +1,271 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects import ( + ProjectModelPermissions, + ProjectModelPermissionsDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestModelPermissions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + model_permission = client.admin.organization.projects.model_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.model_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + model_permission = client.admin.organization.projects.model_permissions.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.model_permissions.with_streaming_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="", + mode="allow_list", + model_ids=["string"], + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + model_permission = client.admin.organization.projects.model_permissions.delete( + "project_id", + ) + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.model_permissions.with_raw_response.delete( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.model_permissions.with_streaming_response.delete( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.model_permissions.with_raw_response.delete( + "", + ) + + +class TestAsyncModelPermissions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + model_permission = await async_client.admin.organization.projects.model_permissions.retrieve( + "project_id", + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.model_permissions.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = await response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.model_permissions.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + model_permission = await async_client.admin.organization.projects.model_permissions.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.model_permissions.with_streaming_response.update( + project_id="project_id", + mode="allow_list", + model_ids=["string"], + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = await response.parse() + assert_matches_type(ProjectModelPermissions, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.model_permissions.with_raw_response.update( + project_id="", + mode="allow_list", + model_ids=["string"], + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + model_permission = await async_client.admin.organization.projects.model_permissions.delete( + "project_id", + ) + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.model_permissions.with_raw_response.delete( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + model_permission = response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.model_permissions.with_streaming_response.delete( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + model_permission = await response.parse() + assert_matches_type(ProjectModelPermissionsDeleted, model_permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.model_permissions.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/test_usage.py b/tests/api_resources/admin/organization/test_usage.py index f9c4a8e2c5..b7aef88dcb 100644 --- a/tests/api_resources/admin/organization/test_usage.py +++ b/tests/api_resources/admin/organization/test_usage.py @@ -17,6 +17,8 @@ UsageModerationsResponse, UsageVectorStoresResponse, UsageAudioSpeechesResponse, + UsageWebSearchCallsResponse, + UsageFileSearchCallsResponse, UsageAudioTranscriptionsResponse, UsageCodeInterpreterSessionsResponse, ) @@ -305,6 +307,53 @@ def test_streaming_response_embeddings(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_file_search_calls(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.file_search_calls( + start_time=0, + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_method_file_search_calls_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.file_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + user_ids=["string"], + vector_store_ids=["string"], + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_file_search_calls(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.file_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_file_search_calls(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.file_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize def test_method_images(self, client: OpenAI) -> None: usage = client.admin.organization.usage.images( @@ -445,6 +494,54 @@ def test_streaming_response_vector_stores(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_web_search_calls(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.web_search_calls( + start_time=0, + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_method_web_search_calls_with_all_params(self, client: OpenAI) -> None: + usage = client.admin.organization.usage.web_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + context_levels=["low"], + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_raw_response_web_search_calls(self, client: OpenAI) -> None: + response = client.admin.organization.usage.with_raw_response.web_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + def test_streaming_response_web_search_calls(self, client: OpenAI) -> None: + with client.admin.organization.usage.with_streaming_response.web_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncUsage: parametrize = pytest.mark.parametrize( @@ -729,6 +826,53 @@ async def test_streaming_response_embeddings(self, async_client: AsyncOpenAI) -> assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_file_search_calls(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.file_search_calls( + start_time=0, + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_method_file_search_calls_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.file_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + end_time=0, + group_by=["project_id"], + limit=0, + page="page", + project_ids=["string"], + user_ids=["string"], + vector_store_ids=["string"], + ) + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_file_search_calls(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.file_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_file_search_calls(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.file_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageFileSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True + @parametrize async def test_method_images(self, async_client: AsyncOpenAI) -> None: usage = await async_client.admin.organization.usage.images( @@ -868,3 +1012,51 @@ async def test_streaming_response_vector_stores(self, async_client: AsyncOpenAI) assert_matches_type(UsageVectorStoresResponse, usage, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_web_search_calls(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.web_search_calls( + start_time=0, + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_method_web_search_calls_with_all_params(self, async_client: AsyncOpenAI) -> None: + usage = await async_client.admin.organization.usage.web_search_calls( + start_time=0, + api_key_ids=["string"], + bucket_width="1m", + context_levels=["low"], + end_time=0, + group_by=["project_id"], + limit=0, + models=["string"], + page="page", + project_ids=["string"], + user_ids=["string"], + ) + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_raw_response_web_search_calls(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.usage.with_raw_response.web_search_calls( + start_time=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage = response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + @parametrize + async def test_streaming_response_web_search_calls(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.usage.with_streaming_response.web_search_calls( + start_time=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage = await response.parse() + assert_matches_type(UsageWebSearchCallsResponse, usage, path=["response"]) + + assert cast(Any, response.is_closed) is True From bab18af787cd5d962aedeb4b5b86df4f6cf28003 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 19:53:09 +0000 Subject: [PATCH 366/408] chore(api): docs updates --- .stats.yml | 6 +++--- src/openai/types/responses/response_function_web_search.py | 2 +- .../types/responses/response_function_web_search_param.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index 60770c0e7a..fb3f316cec 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 240 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-d7d5adb20c516e7aca6e35ed9f58aba0bc14dd5aa0096fd980061c4a5ab406e3.yml -openapi_spec_hash: 72ee7d4fe582e75d16e67b6c97881fed -config_hash: af72288617facaeed4d014024a9c946d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-66f110677cd924205b0a4e2a1f567c8b56349ee9658bd0ce86561a413a2aabfe.yml +openapi_spec_hash: ab8f320a7a6b2d30d5f1665b8a8bf84e +config_hash: 425042cc0e99cf31866f6c98fef2be27 diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index de6001e146..d3d4afbee3 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -46,7 +46,7 @@ class ActionOpenPage(BaseModel): """Action type "open_page" - Opens a specific URL from search results.""" type: Literal["open_page"] - """The action type.""" + """The action type. Always `open_page`.""" url: Optional[str] = None """The URL opened by the model.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 15e313b0d3..e0d50757a3 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -47,7 +47,7 @@ class ActionOpenPage(TypedDict, total=False): """Action type "open_page" - Opens a specific URL from search results.""" type: Required[Literal["open_page"]] - """The action type.""" + """The action type. Always `open_page`.""" url: Optional[str] """The URL opened by the model.""" From 74978f055a7adf004dec718e80bb46241e54d9ca Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:33:19 -0400 Subject: [PATCH 367/408] chore: trigger release automation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 90f1c2cbd9..13b508e81f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # OpenAI Python API library + + [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From 48888380cdfc01e4f22f9ed7fbd5250231472e0d Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:41:41 -0400 Subject: [PATCH 368/408] chore: remove release automation trigger --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 13b508e81f..90f1c2cbd9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # OpenAI Python API library - - [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From d4a322816ad637330e40fdcdee9ca48bc92a2a4f Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:50:17 -0400 Subject: [PATCH 369/408] chore: check release PR custom code sync --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 90f1c2cbd9..8a956145c2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # OpenAI Python API library + + [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From d881c67866083ae187e14664e289e68a3ba04686 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 19 May 2026 16:55:01 -0400 Subject: [PATCH 370/408] Revert "chore: check release PR custom code sync" This reverts commit 2638779a5b8fffaa8fdb6eebc1d734f15d2491f8. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 8a956145c2..90f1c2cbd9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # OpenAI Python API library - - [![PyPI version](https://img.shields.io/pypi/v/openai.svg?label=pypi%20(stable))](https://pypi.org/project/openai/) From b85b647b5312debb951814dfb9ed13f906d6bf43 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 07:31:42 +0000 Subject: [PATCH 371/408] feat(api): api update --- .stats.yml | 8 +- api.md | 71 +++ .../resources/admin/organization/__init__.py | 28 + .../admin/organization/data_retention.py | 245 ++++++++ .../admin/organization/groups/groups.py | 86 +++ .../admin/organization/groups/roles.py | 93 +++ .../admin/organization/groups/users.py | 93 +++ .../admin/organization/organization.py | 64 ++ .../admin/organization/projects/__init__.py | 28 + .../organization/projects/data_retention.py | 283 +++++++++ .../organization/projects/groups/groups.py | 108 +++- .../organization/projects/groups/roles.py | 109 ++++ .../admin/organization/projects/projects.py | 64 ++ .../admin/organization/projects/roles.py | 92 +++ .../organization/projects/service_accounts.py | 134 +++- .../organization/projects/spend_alerts.py | 589 ++++++++++++++++++ .../organization/projects/users/roles.py | 109 ++++ .../resources/admin/organization/roles.py | 86 +++ .../admin/organization/spend_alerts.py | 553 ++++++++++++++++ .../admin/organization/users/roles.py | 93 +++ .../types/admin/organization/__init__.py | 7 + .../data_retention_update_params.py | 19 + src/openai/types/admin/organization/group.py | 4 +- .../admin/organization/groups/__init__.py | 2 + .../organization/groups/role_list_response.py | 11 +- .../groups/role_retrieve_response.py | 55 ++ .../groups/user_retrieve_response.py | 30 + .../organization_data_retention.py | 22 + .../organization/organization_spend_alert.py | 43 ++ .../organization_spend_alert_deleted.py | 20 + .../admin/organization/projects/__init__.py | 9 + .../projects/data_retention_update_params.py | 21 + .../projects/group_retrieve_params.py | 14 + .../organization/projects/groups/__init__.py | 1 + .../projects/groups/role_list_response.py | 11 +- .../projects/groups/role_retrieve_response.py | 55 ++ .../projects/project_data_retention.py | 24 + .../organization/projects/project_group.py | 2 +- .../projects/project_spend_alert.py | 43 ++ .../projects/project_spend_alert_deleted.py | 20 + .../projects/service_account_update_params.py | 17 + .../projects/spend_alert_create_params.py | 37 ++ .../projects/spend_alert_list_params.py | 29 + .../projects/spend_alert_update_params.py | 39 ++ .../organization/projects/users/__init__.py | 1 + .../projects/users/role_list_response.py | 11 +- .../projects/users/role_retrieve_response.py | 55 ++ .../organization/spend_alert_create_params.py | 37 ++ .../organization/spend_alert_list_params.py | 29 + .../organization/spend_alert_update_params.py | 37 ++ .../admin/organization/users/__init__.py | 1 + .../organization/users/role_list_response.py | 11 +- .../users/role_retrieve_response.py | 55 ++ .../responses/response_function_web_search.py | 2 +- .../response_function_web_search_param.py | 2 +- .../admin/organization/groups/test_roles.py | 97 +++ .../admin/organization/groups/test_users.py | 97 +++ .../projects/groups/test_roles.py | 121 ++++ .../projects/test_data_retention.py | 184 ++++++ .../organization/projects/test_groups.py | 114 ++++ .../admin/organization/projects/test_roles.py | 96 +++ .../projects/test_service_accounts.py | 116 ++++ .../projects/test_spend_alerts.py | 582 +++++++++++++++++ .../organization/projects/users/test_roles.py | 121 ++++ .../admin/organization/test_data_retention.py | 136 ++++ .../admin/organization/test_groups.py | 76 +++ .../admin/organization/test_roles.py | 76 +++ .../admin/organization/test_spend_alerts.py | 462 ++++++++++++++ .../admin/organization/users/test_roles.py | 97 +++ 69 files changed, 6073 insertions(+), 14 deletions(-) create mode 100644 src/openai/resources/admin/organization/data_retention.py create mode 100644 src/openai/resources/admin/organization/projects/data_retention.py create mode 100644 src/openai/resources/admin/organization/projects/spend_alerts.py create mode 100644 src/openai/resources/admin/organization/spend_alerts.py create mode 100644 src/openai/types/admin/organization/data_retention_update_params.py create mode 100644 src/openai/types/admin/organization/groups/role_retrieve_response.py create mode 100644 src/openai/types/admin/organization/groups/user_retrieve_response.py create mode 100644 src/openai/types/admin/organization/organization_data_retention.py create mode 100644 src/openai/types/admin/organization/organization_spend_alert.py create mode 100644 src/openai/types/admin/organization/organization_spend_alert_deleted.py create mode 100644 src/openai/types/admin/organization/projects/data_retention_update_params.py create mode 100644 src/openai/types/admin/organization/projects/group_retrieve_params.py create mode 100644 src/openai/types/admin/organization/projects/groups/role_retrieve_response.py create mode 100644 src/openai/types/admin/organization/projects/project_data_retention.py create mode 100644 src/openai/types/admin/organization/projects/project_spend_alert.py create mode 100644 src/openai/types/admin/organization/projects/project_spend_alert_deleted.py create mode 100644 src/openai/types/admin/organization/projects/service_account_update_params.py create mode 100644 src/openai/types/admin/organization/projects/spend_alert_create_params.py create mode 100644 src/openai/types/admin/organization/projects/spend_alert_list_params.py create mode 100644 src/openai/types/admin/organization/projects/spend_alert_update_params.py create mode 100644 src/openai/types/admin/organization/projects/users/role_retrieve_response.py create mode 100644 src/openai/types/admin/organization/spend_alert_create_params.py create mode 100644 src/openai/types/admin/organization/spend_alert_list_params.py create mode 100644 src/openai/types/admin/organization/spend_alert_update_params.py create mode 100644 src/openai/types/admin/organization/users/role_retrieve_response.py create mode 100644 tests/api_resources/admin/organization/projects/test_data_retention.py create mode 100644 tests/api_resources/admin/organization/projects/test_spend_alerts.py create mode 100644 tests/api_resources/admin/organization/test_data_retention.py create mode 100644 tests/api_resources/admin/organization/test_spend_alerts.py diff --git a/.stats.yml b/.stats.yml index fb3f316cec..b9d21daa06 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 240 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-66f110677cd924205b0a4e2a1f567c8b56349ee9658bd0ce86561a413a2aabfe.yml -openapi_spec_hash: ab8f320a7a6b2d30d5f1665b8a8bf84e -config_hash: 425042cc0e99cf31866f6c98fef2be27 +configured_endpoints: 262 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-afacc4343d0efc074c8c5667eb83570642c8b9acaa7792ca8e075c6d18ef9f3a.yml +openapi_spec_hash: a62a557c61532681963fd21e748b0eb4 +config_hash: bb69d8d0771dbac4a84fc6dca11e3ceb diff --git a/api.md b/api.md index ab2f3f75a2..10a729d995 100644 --- a/api.md +++ b/api.md @@ -813,6 +813,7 @@ Types: ```python from openai.types.admin.organization.users import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -821,6 +822,7 @@ from openai.types.admin.organization.users import ( Methods: - client.admin.organization.users.roles.create(user_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.users.roles.retrieve(role_id, \*, user_id) -> RoleRetrieveResponse - client.admin.organization.users.roles.list(user_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.users.roles.delete(role_id, \*, user_id) -> RoleDeleteResponse @@ -835,6 +837,7 @@ from openai.types.admin.organization import Group, GroupUpdateResponse, GroupDel Methods: - client.admin.organization.groups.create(\*\*params) -> Group +- client.admin.organization.groups.retrieve(group_id) -> Group - client.admin.organization.groups.update(group_id, \*\*params) -> GroupUpdateResponse - client.admin.organization.groups.list(\*\*params) -> SyncNextCursorPage[Group] - client.admin.organization.groups.delete(group_id) -> GroupDeleteResponse @@ -847,6 +850,7 @@ Types: from openai.types.admin.organization.groups import ( OrganizationGroupUser, UserCreateResponse, + UserRetrieveResponse, UserDeleteResponse, ) ``` @@ -854,6 +858,7 @@ from openai.types.admin.organization.groups import ( Methods: - client.admin.organization.groups.users.create(group_id, \*\*params) -> UserCreateResponse +- client.admin.organization.groups.users.retrieve(user_id, \*, group_id) -> UserRetrieveResponse - client.admin.organization.groups.users.list(group_id, \*\*params) -> SyncNextCursorPage[OrganizationGroupUser] - client.admin.organization.groups.users.delete(user_id, \*, group_id) -> UserDeleteResponse @@ -864,6 +869,7 @@ Types: ```python from openai.types.admin.organization.groups import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -872,6 +878,7 @@ from openai.types.admin.organization.groups import ( Methods: - client.admin.organization.groups.roles.create(group_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.groups.roles.retrieve(role_id, \*, group_id) -> RoleRetrieveResponse - client.admin.organization.groups.roles.list(group_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.groups.roles.delete(role_id, \*, group_id) -> RoleDeleteResponse @@ -886,10 +893,39 @@ from openai.types.admin.organization import Role, RoleDeleteResponse Methods: - client.admin.organization.roles.create(\*\*params) -> Role +- client.admin.organization.roles.retrieve(role_id) -> Role - client.admin.organization.roles.update(role_id, \*\*params) -> Role - client.admin.organization.roles.list(\*\*params) -> SyncNextCursorPage[Role] - client.admin.organization.roles.delete(role_id) -> RoleDeleteResponse +### DataRetention + +Types: + +```python +from openai.types.admin.organization import OrganizationDataRetention +``` + +Methods: + +- client.admin.organization.data_retention.retrieve() -> OrganizationDataRetention +- client.admin.organization.data_retention.update(\*\*params) -> OrganizationDataRetention + +### SpendAlerts + +Types: + +```python +from openai.types.admin.organization import OrganizationSpendAlert, OrganizationSpendAlertDeleted +``` + +Methods: + +- client.admin.organization.spend_alerts.create(\*\*params) -> OrganizationSpendAlert +- client.admin.organization.spend_alerts.update(alert_id, \*\*params) -> OrganizationSpendAlert +- client.admin.organization.spend_alerts.list(\*\*params) -> SyncConversationCursorPage[OrganizationSpendAlert] +- client.admin.organization.spend_alerts.delete(alert_id) -> OrganizationSpendAlertDeleted + ### Certificates Types: @@ -953,6 +989,7 @@ Types: ```python from openai.types.admin.organization.projects.users import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -961,6 +998,7 @@ from openai.types.admin.organization.projects.users import ( Methods: - client.admin.organization.projects.users.roles.create(user_id, \*, project_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.projects.users.roles.retrieve(role_id, \*, project_id, user_id) -> RoleRetrieveResponse - client.admin.organization.projects.users.roles.list(user_id, \*, project_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.projects.users.roles.delete(role_id, \*, project_id, user_id) -> RoleDeleteResponse @@ -980,6 +1018,7 @@ Methods: - client.admin.organization.projects.service_accounts.create(project_id, \*\*params) -> ServiceAccountCreateResponse - client.admin.organization.projects.service_accounts.retrieve(service_account_id, \*, project_id) -> ProjectServiceAccount +- client.admin.organization.projects.service_accounts.update(service_account_id, \*, project_id, \*\*params) -> ProjectServiceAccount - client.admin.organization.projects.service_accounts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectServiceAccount] - client.admin.organization.projects.service_accounts.delete(service_account_id, \*, project_id) -> ServiceAccountDeleteResponse @@ -1051,6 +1090,7 @@ from openai.types.admin.organization.projects import ProjectGroup, GroupDeleteRe Methods: - client.admin.organization.projects.groups.create(project_id, \*\*params) -> ProjectGroup +- client.admin.organization.projects.groups.retrieve(group_id, \*, project_id, \*\*params) -> ProjectGroup - client.admin.organization.projects.groups.list(project_id, \*\*params) -> SyncNextCursorPage[ProjectGroup] - client.admin.organization.projects.groups.delete(group_id, \*, project_id) -> GroupDeleteResponse @@ -1061,6 +1101,7 @@ Types: ```python from openai.types.admin.organization.projects.groups import ( RoleCreateResponse, + RoleRetrieveResponse, RoleListResponse, RoleDeleteResponse, ) @@ -1069,6 +1110,7 @@ from openai.types.admin.organization.projects.groups import ( Methods: - client.admin.organization.projects.groups.roles.create(group_id, \*, project_id, \*\*params) -> RoleCreateResponse +- client.admin.organization.projects.groups.roles.retrieve(role_id, \*, project_id, group_id) -> RoleRetrieveResponse - client.admin.organization.projects.groups.roles.list(group_id, \*, project_id, \*\*params) -> SyncNextCursorPage[RoleListResponse] - client.admin.organization.projects.groups.roles.delete(role_id, \*, project_id, group_id) -> RoleDeleteResponse @@ -1083,10 +1125,39 @@ from openai.types.admin.organization.projects import RoleDeleteResponse Methods: - client.admin.organization.projects.roles.create(project_id, \*\*params) -> Role +- client.admin.organization.projects.roles.retrieve(role_id, \*, project_id) -> Role - client.admin.organization.projects.roles.update(role_id, \*, project_id, \*\*params) -> Role - client.admin.organization.projects.roles.list(project_id, \*\*params) -> SyncNextCursorPage[Role] - client.admin.organization.projects.roles.delete(role_id, \*, project_id) -> RoleDeleteResponse +#### DataRetention + +Types: + +```python +from openai.types.admin.organization.projects import ProjectDataRetention +``` + +Methods: + +- client.admin.organization.projects.data_retention.retrieve(project_id) -> ProjectDataRetention +- client.admin.organization.projects.data_retention.update(project_id, \*\*params) -> ProjectDataRetention + +#### SpendAlerts + +Types: + +```python +from openai.types.admin.organization.projects import ProjectSpendAlert, ProjectSpendAlertDeleted +``` + +Methods: + +- client.admin.organization.projects.spend_alerts.create(project_id, \*\*params) -> ProjectSpendAlert +- client.admin.organization.projects.spend_alerts.update(alert_id, \*, project_id, \*\*params) -> ProjectSpendAlert +- client.admin.organization.projects.spend_alerts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectSpendAlert] +- client.admin.organization.projects.spend_alerts.delete(alert_id, \*, project_id) -> ProjectSpendAlertDeleted + #### Certificates Types: diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py index 50641088bb..d87fbbc81d 100644 --- a/src/openai/resources/admin/organization/__init__.py +++ b/src/openai/resources/admin/organization/__init__.py @@ -72,6 +72,14 @@ OrganizationWithStreamingResponse, AsyncOrganizationWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) from .admin_api_keys import ( AdminAPIKeys, AsyncAdminAPIKeys, @@ -80,6 +88,14 @@ AdminAPIKeysWithStreamingResponse, AsyncAdminAPIKeysWithStreamingResponse, ) +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) __all__ = [ "AuditLogs", @@ -124,6 +140,18 @@ "AsyncRolesWithRawResponse", "RolesWithStreamingResponse", "AsyncRolesWithStreamingResponse", + "DataRetention", + "AsyncDataRetention", + "DataRetentionWithRawResponse", + "AsyncDataRetentionWithRawResponse", + "DataRetentionWithStreamingResponse", + "AsyncDataRetentionWithStreamingResponse", + "SpendAlerts", + "AsyncSpendAlerts", + "SpendAlertsWithRawResponse", + "AsyncSpendAlertsWithRawResponse", + "SpendAlertsWithStreamingResponse", + "AsyncSpendAlertsWithStreamingResponse", "Certificates", "AsyncCertificates", "CertificatesWithRawResponse", diff --git a/src/openai/resources/admin/organization/data_retention.py b/src/openai/resources/admin/organization/data_retention.py new file mode 100644 index 0000000000..c3b7325a92 --- /dev/null +++ b/src/openai/resources/admin/organization/data_retention.py @@ -0,0 +1,245 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.admin.organization import data_retention_update_params +from ....types.admin.organization.organization_data_retention import OrganizationDataRetention + +__all__ = ["DataRetention", "AsyncDataRetention"] + + +class DataRetention(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return DataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return DataRetentionWithStreamingResponse(self) + + def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """Retrieves organization data retention controls.""" + return self._get( + "/organization/data_retention", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + def update( + self, + *, + retention_type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """ + Updates organization data retention controls. + + Args: + retention_type: The desired organization data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/data_retention", + body=maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + +class AsyncDataRetention(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncDataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncDataRetentionWithStreamingResponse(self) + + async def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """Retrieves organization data retention controls.""" + return await self._get( + "/organization/data_retention", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + async def update( + self, + *, + retention_type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationDataRetention: + """ + Updates organization data retention controls. + + Args: + retention_type: The desired organization data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/data_retention", + body=await async_maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationDataRetention, + ) + + +class DataRetentionWithRawResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithRawResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + data_retention.update, + ) + + +class DataRetentionWithStreamingResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = to_streamed_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithStreamingResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = async_to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + data_retention.update, + ) diff --git a/src/openai/resources/admin/organization/groups/groups.py b/src/openai/resources/admin/organization/groups/groups.py index 80baa38926..fa7a6b2354 100644 --- a/src/openai/resources/admin/organization/groups/groups.py +++ b/src/openai/resources/admin/organization/groups/groups.py @@ -104,6 +104,43 @@ def create( cast_to=Group, ) + def retrieve( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Retrieves a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + def update( self, group_id: str, @@ -305,6 +342,43 @@ async def create( cast_to=Group, ) + async def retrieve( + self, + group_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Group: + """ + Retrieves a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}", group_id=group_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Group, + ) + async def update( self, group_id: str, @@ -447,6 +521,9 @@ def __init__(self, groups: Groups) -> None: self.create = _legacy_response.to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + groups.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( groups.update, ) @@ -473,6 +550,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + groups.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( groups.update, ) @@ -499,6 +579,9 @@ def __init__(self, groups: Groups) -> None: self.create = to_streamed_response_wrapper( groups.create, ) + self.retrieve = to_streamed_response_wrapper( + groups.retrieve, + ) self.update = to_streamed_response_wrapper( groups.update, ) @@ -525,6 +608,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = async_to_streamed_response_wrapper( groups.create, ) + self.retrieve = async_to_streamed_response_wrapper( + groups.retrieve, + ) self.update = async_to_streamed_response_wrapper( groups.update, ) diff --git a/src/openai/resources/admin/organization/groups/roles.py b/src/openai/resources/admin/organization/groups/roles.py index c85efccfef..c4405c487c 100644 --- a/src/openai/resources/admin/organization/groups/roles.py +++ b/src/openai/resources/admin/organization/groups/roles.py @@ -18,6 +18,7 @@ from .....types.admin.organization.groups.role_list_response import RoleListResponse from .....types.admin.organization.groups.role_create_response import RoleCreateResponse from .....types.admin.organization.groups.role_delete_response import RoleDeleteResponse +from .....types.admin.organization.groups.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -83,6 +84,46 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -241,6 +282,46 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}/roles/{role_id}", group_id=group_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -345,6 +426,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -360,6 +444,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -375,6 +462,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -390,6 +480,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/resources/admin/organization/groups/users.py b/src/openai/resources/admin/organization/groups/users.py index 89a2996e13..e0ac71afd9 100644 --- a/src/openai/resources/admin/organization/groups/users.py +++ b/src/openai/resources/admin/organization/groups/users.py @@ -17,6 +17,7 @@ from .....types.admin.organization.groups import user_list_params, user_create_params from .....types.admin.organization.groups.user_create_response import UserCreateResponse from .....types.admin.organization.groups.user_delete_response import UserDeleteResponse +from .....types.admin.organization.groups.user_retrieve_response import UserRetrieveResponse from .....types.admin.organization.groups.organization_group_user import OrganizationGroupUser __all__ = ["Users", "AsyncUsers"] @@ -83,6 +84,46 @@ def create( cast_to=UserCreateResponse, ) + def retrieve( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserRetrieveResponse: + """ + Retrieves a user in a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return self._get( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserRetrieveResponse, + ) + def list( self, group_id: str, @@ -242,6 +283,46 @@ async def create( cast_to=UserCreateResponse, ) + async def retrieve( + self, + user_id: str, + *, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UserRetrieveResponse: + """ + Retrieves a user in a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + return await self._get( + path_template("/organization/groups/{group_id}/users/{user_id}", group_id=group_id, user_id=user_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=UserRetrieveResponse, + ) + def list( self, group_id: str, @@ -347,6 +428,9 @@ def __init__(self, users: Users) -> None: self.create = _legacy_response.to_raw_response_wrapper( users.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + users.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( users.list, ) @@ -362,6 +446,9 @@ def __init__(self, users: AsyncUsers) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( users.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + users.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( users.list, ) @@ -377,6 +464,9 @@ def __init__(self, users: Users) -> None: self.create = to_streamed_response_wrapper( users.create, ) + self.retrieve = to_streamed_response_wrapper( + users.retrieve, + ) self.list = to_streamed_response_wrapper( users.list, ) @@ -392,6 +482,9 @@ def __init__(self, users: AsyncUsers) -> None: self.create = async_to_streamed_response_wrapper( users.create, ) + self.retrieve = async_to_streamed_response_wrapper( + users.retrieve, + ) self.list = async_to_streamed_response_wrapper( users.list, ) diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py index abbbadf575..62bba207cb 100644 --- a/src/openai/resources/admin/organization/organization.py +++ b/src/openai/resources/admin/organization/organization.py @@ -52,6 +52,14 @@ CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) from .groups.groups import ( Groups, AsyncGroups, @@ -68,6 +76,14 @@ AdminAPIKeysWithStreamingResponse, AsyncAdminAPIKeysWithStreamingResponse, ) +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) from .projects.projects import ( Projects, AsyncProjects, @@ -110,6 +126,14 @@ def groups(self) -> Groups: def roles(self) -> Roles: return Roles(self._client) + @cached_property + def data_retention(self) -> DataRetention: + return DataRetention(self._client) + + @cached_property + def spend_alerts(self) -> SpendAlerts: + return SpendAlerts(self._client) + @cached_property def certificates(self) -> Certificates: return Certificates(self._client) @@ -168,6 +192,14 @@ def groups(self) -> AsyncGroups: def roles(self) -> AsyncRoles: return AsyncRoles(self._client) + @cached_property + def data_retention(self) -> AsyncDataRetention: + return AsyncDataRetention(self._client) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlerts: + return AsyncSpendAlerts(self._client) + @cached_property def certificates(self) -> AsyncCertificates: return AsyncCertificates(self._client) @@ -229,6 +261,14 @@ def groups(self) -> GroupsWithRawResponse: def roles(self) -> RolesWithRawResponse: return RolesWithRawResponse(self._organization.roles) + @cached_property + def data_retention(self) -> DataRetentionWithRawResponse: + return DataRetentionWithRawResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithRawResponse: + return SpendAlertsWithRawResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithRawResponse: return CertificatesWithRawResponse(self._organization.certificates) @@ -271,6 +311,14 @@ def groups(self) -> AsyncGroupsWithRawResponse: def roles(self) -> AsyncRolesWithRawResponse: return AsyncRolesWithRawResponse(self._organization.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithRawResponse: + return AsyncDataRetentionWithRawResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: + return AsyncSpendAlertsWithRawResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithRawResponse: return AsyncCertificatesWithRawResponse(self._organization.certificates) @@ -313,6 +361,14 @@ def groups(self) -> GroupsWithStreamingResponse: def roles(self) -> RolesWithStreamingResponse: return RolesWithStreamingResponse(self._organization.roles) + @cached_property + def data_retention(self) -> DataRetentionWithStreamingResponse: + return DataRetentionWithStreamingResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithStreamingResponse: + return SpendAlertsWithStreamingResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithStreamingResponse: return CertificatesWithStreamingResponse(self._organization.certificates) @@ -355,6 +411,14 @@ def groups(self) -> AsyncGroupsWithStreamingResponse: def roles(self) -> AsyncRolesWithStreamingResponse: return AsyncRolesWithStreamingResponse(self._organization.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: + return AsyncDataRetentionWithStreamingResponse(self._organization.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: + return AsyncSpendAlertsWithStreamingResponse(self._organization.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithStreamingResponse: return AsyncCertificatesWithStreamingResponse(self._organization.certificates) diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py index 7f77863a2d..3bc97170fd 100644 --- a/src/openai/resources/admin/organization/projects/__init__.py +++ b/src/openai/resources/admin/organization/projects/__init__.py @@ -56,6 +56,22 @@ CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) from .service_accounts import ( ServiceAccounts, AsyncServiceAccounts, @@ -130,6 +146,18 @@ "AsyncRolesWithRawResponse", "RolesWithStreamingResponse", "AsyncRolesWithStreamingResponse", + "DataRetention", + "AsyncDataRetention", + "DataRetentionWithRawResponse", + "AsyncDataRetentionWithRawResponse", + "DataRetentionWithStreamingResponse", + "AsyncDataRetentionWithStreamingResponse", + "SpendAlerts", + "AsyncSpendAlerts", + "SpendAlertsWithRawResponse", + "AsyncSpendAlertsWithRawResponse", + "SpendAlertsWithStreamingResponse", + "AsyncSpendAlertsWithStreamingResponse", "Certificates", "AsyncCertificates", "CertificatesWithRawResponse", diff --git a/src/openai/resources/admin/organization/projects/data_retention.py b/src/openai/resources/admin/organization/projects/data_retention.py new file mode 100644 index 0000000000..2d0df3e7e2 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/data_retention.py @@ -0,0 +1,283 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import data_retention_update_params +from .....types.admin.organization.projects.project_data_retention import ProjectDataRetention + +__all__ = ["DataRetention", "AsyncDataRetention"] + + +class DataRetention(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return DataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return DataRetentionWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Retrieves project data retention controls. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + def update( + self, + project_id: str, + *, + retention_type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Updates project data retention controls. + + Args: + retention_type: The desired project data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + body=maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + +class AsyncDataRetention(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDataRetentionWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncDataRetentionWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDataRetentionWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncDataRetentionWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Retrieves project data retention controls. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + async def update( + self, + project_id: str, + *, + retention_type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDataRetention: + """ + Updates project data retention controls. + + Args: + retention_type: The desired project data retention type. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/data_retention", project_id=project_id), + body=await async_maybe_transform( + {"retention_type": retention_type}, data_retention_update_params.DataRetentionUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectDataRetention, + ) + + +class DataRetentionWithRawResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithRawResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + data_retention.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + data_retention.update, + ) + + +class DataRetentionWithStreamingResponse: + def __init__(self, data_retention: DataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = to_streamed_response_wrapper( + data_retention.update, + ) + + +class AsyncDataRetentionWithStreamingResponse: + def __init__(self, data_retention: AsyncDataRetention) -> None: + self._data_retention = data_retention + + self.retrieve = async_to_streamed_response_wrapper( + data_retention.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + data_retention.update, + ) diff --git a/src/openai/resources/admin/organization/projects/groups/groups.py b/src/openai/resources/admin/organization/projects/groups/groups.py index aad9bfd6ec..a7ef36fd64 100644 --- a/src/openai/resources/admin/organization/projects/groups/groups.py +++ b/src/openai/resources/admin/organization/projects/groups/groups.py @@ -22,7 +22,7 @@ from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ......pagination import SyncNextCursorPage, AsyncNextCursorPage from ......_base_client import AsyncPaginator, make_request_options -from ......types.admin.organization.projects import group_list_params, group_create_params +from ......types.admin.organization.projects import group_list_params, group_create_params, group_retrieve_params from ......types.admin.organization.projects.project_group import ProjectGroup from ......types.admin.organization.projects.group_delete_response import GroupDeleteResponse @@ -103,6 +103,52 @@ def create( cast_to=ProjectGroup, ) + def retrieve( + self, + group_id: str, + *, + project_id: str, + group_type: Literal["group", "tenant_group"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Retrieves a project's group. + + Args: + group_type: The type of group to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"group_type": group_type}, group_retrieve_params.GroupRetrieveParams), + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + def list( self, project_id: str, @@ -276,6 +322,54 @@ async def create( cast_to=ProjectGroup, ) + async def retrieve( + self, + group_id: str, + *, + project_id: str, + group_type: Literal["group", "tenant_group"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectGroup: + """ + Retrieves a project's group. + + Args: + group_type: The type of group to retrieve. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/groups/{group_id}", project_id=project_id, group_id=group_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"group_type": group_type}, group_retrieve_params.GroupRetrieveParams + ), + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectGroup, + ) + def list( self, project_id: str, @@ -382,6 +476,9 @@ def __init__(self, groups: Groups) -> None: self.create = _legacy_response.to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + groups.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( groups.list, ) @@ -401,6 +498,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( groups.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + groups.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( groups.list, ) @@ -420,6 +520,9 @@ def __init__(self, groups: Groups) -> None: self.create = to_streamed_response_wrapper( groups.create, ) + self.retrieve = to_streamed_response_wrapper( + groups.retrieve, + ) self.list = to_streamed_response_wrapper( groups.list, ) @@ -439,6 +542,9 @@ def __init__(self, groups: AsyncGroups) -> None: self.create = async_to_streamed_response_wrapper( groups.create, ) + self.retrieve = async_to_streamed_response_wrapper( + groups.retrieve, + ) self.list = async_to_streamed_response_wrapper( groups.list, ) diff --git a/src/openai/resources/admin/organization/projects/groups/roles.py b/src/openai/resources/admin/organization/projects/groups/roles.py index e3fe3b54fe..5961d05e70 100644 --- a/src/openai/resources/admin/organization/projects/groups/roles.py +++ b/src/openai/resources/admin/organization/projects/groups/roles.py @@ -18,6 +18,7 @@ from ......types.admin.organization.projects.groups.role_list_response import RoleListResponse from ......types.admin.organization.projects.groups.role_create_response import RoleCreateResponse from ......types.admin.organization.projects.groups.role_delete_response import RoleDeleteResponse +from ......types.admin.organization.projects.groups.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -86,6 +87,54 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -258,6 +307,54 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + project_id: str, + group_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a group. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not group_id: + raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template( + "/projects/{project_id}/groups/{group_id}/roles/{role_id}", + project_id=project_id, + group_id=group_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, group_id: str, @@ -373,6 +470,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -388,6 +488,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -403,6 +506,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -418,6 +524,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py index b69e69d0dd..6ffd4c0f85 100644 --- a/src/openai/resources/admin/organization/projects/projects.py +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -50,6 +50,14 @@ CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) +from .spend_alerts import ( + SpendAlerts, + AsyncSpendAlerts, + SpendAlertsWithRawResponse, + AsyncSpendAlertsWithRawResponse, + SpendAlertsWithStreamingResponse, + AsyncSpendAlertsWithStreamingResponse, +) from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .groups.groups import ( @@ -61,6 +69,14 @@ AsyncGroupsWithStreamingResponse, ) from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from .data_retention import ( + DataRetention, + AsyncDataRetention, + DataRetentionWithRawResponse, + AsyncDataRetentionWithRawResponse, + DataRetentionWithStreamingResponse, + AsyncDataRetentionWithStreamingResponse, +) from ....._base_client import AsyncPaginator, make_request_options from .service_accounts import ( ServiceAccounts, @@ -125,6 +141,14 @@ def groups(self) -> Groups: def roles(self) -> Roles: return Roles(self._client) + @cached_property + def data_retention(self) -> DataRetention: + return DataRetention(self._client) + + @cached_property + def spend_alerts(self) -> SpendAlerts: + return SpendAlerts(self._client) + @cached_property def certificates(self) -> Certificates: return Certificates(self._client) @@ -426,6 +450,14 @@ def groups(self) -> AsyncGroups: def roles(self) -> AsyncRoles: return AsyncRoles(self._client) + @cached_property + def data_retention(self) -> AsyncDataRetention: + return AsyncDataRetention(self._client) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlerts: + return AsyncSpendAlerts(self._client) + @cached_property def certificates(self) -> AsyncCertificates: return AsyncCertificates(self._client) @@ -746,6 +778,14 @@ def groups(self) -> GroupsWithRawResponse: def roles(self) -> RolesWithRawResponse: return RolesWithRawResponse(self._projects.roles) + @cached_property + def data_retention(self) -> DataRetentionWithRawResponse: + return DataRetentionWithRawResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithRawResponse: + return SpendAlertsWithRawResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithRawResponse: return CertificatesWithRawResponse(self._projects.certificates) @@ -803,6 +843,14 @@ def groups(self) -> AsyncGroupsWithRawResponse: def roles(self) -> AsyncRolesWithRawResponse: return AsyncRolesWithRawResponse(self._projects.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithRawResponse: + return AsyncDataRetentionWithRawResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: + return AsyncSpendAlertsWithRawResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithRawResponse: return AsyncCertificatesWithRawResponse(self._projects.certificates) @@ -860,6 +908,14 @@ def groups(self) -> GroupsWithStreamingResponse: def roles(self) -> RolesWithStreamingResponse: return RolesWithStreamingResponse(self._projects.roles) + @cached_property + def data_retention(self) -> DataRetentionWithStreamingResponse: + return DataRetentionWithStreamingResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> SpendAlertsWithStreamingResponse: + return SpendAlertsWithStreamingResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> CertificatesWithStreamingResponse: return CertificatesWithStreamingResponse(self._projects.certificates) @@ -917,6 +973,14 @@ def groups(self) -> AsyncGroupsWithStreamingResponse: def roles(self) -> AsyncRolesWithStreamingResponse: return AsyncRolesWithStreamingResponse(self._projects.roles) + @cached_property + def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: + return AsyncDataRetentionWithStreamingResponse(self._projects.data_retention) + + @cached_property + def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: + return AsyncSpendAlertsWithStreamingResponse(self._projects.spend_alerts) + @cached_property def certificates(self) -> AsyncCertificatesWithStreamingResponse: return AsyncCertificatesWithStreamingResponse(self._projects.certificates) diff --git a/src/openai/resources/admin/organization/projects/roles.py b/src/openai/resources/admin/organization/projects/roles.py index c958b037bb..4c603ec5b1 100644 --- a/src/openai/resources/admin/organization/projects/roles.py +++ b/src/openai/resources/admin/organization/projects/roles.py @@ -96,6 +96,46 @@ def create( cast_to=Role, ) + def retrieve( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves a project role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + def update( self, role_id: str, @@ -325,6 +365,46 @@ async def create( cast_to=Role, ) + async def retrieve( + self, + role_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves a project role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/projects/{project_id}/roles/{role_id}", project_id=project_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + async def update( self, role_id: str, @@ -487,6 +567,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( roles.update, ) @@ -505,6 +588,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( roles.update, ) @@ -523,6 +609,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.update = to_streamed_response_wrapper( roles.update, ) @@ -541,6 +630,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.update = async_to_streamed_response_wrapper( roles.update, ) diff --git a/src/openai/resources/admin/organization/projects/service_accounts.py b/src/openai/resources/admin/organization/projects/service_accounts.py index 9c265fd766..0de0ced9cf 100644 --- a/src/openai/resources/admin/organization/projects/service_accounts.py +++ b/src/openai/resources/admin/organization/projects/service_accounts.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing_extensions import Literal + import httpx from ..... import _legacy_response @@ -12,7 +14,11 @@ from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage from ....._base_client import AsyncPaginator, make_request_options -from .....types.admin.organization.projects import service_account_list_params, service_account_create_params +from .....types.admin.organization.projects import ( + service_account_list_params, + service_account_create_params, + service_account_update_params, +) from .....types.admin.organization.projects.project_service_account import ProjectServiceAccount from .....types.admin.organization.projects.service_account_create_response import ServiceAccountCreateResponse from .....types.admin.organization.projects.service_account_delete_response import ServiceAccountDeleteResponse @@ -127,6 +133,63 @@ def retrieve( cast_to=ProjectServiceAccount, ) + def update( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + role: Literal["member", "owner"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Updates a service account in the project. + + Args: + name: The updated service account name. + + role: The updated service account role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + body=maybe_transform( + { + "name": name, + "role": role, + }, + service_account_update_params.ServiceAccountUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + def list( self, project_id: str, @@ -337,6 +400,63 @@ async def retrieve( cast_to=ProjectServiceAccount, ) + async def update( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + role: Literal["member", "owner"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectServiceAccount: + """ + Updates a service account in the project. + + Args: + name: The updated service account name. + + role: The updated service account role. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}", + project_id=project_id, + service_account_id=service_account_id, + ), + body=await async_maybe_transform( + { + "name": name, + "role": role, + }, + service_account_update_params.ServiceAccountUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectServiceAccount, + ) + def list( self, project_id: str, @@ -450,6 +570,9 @@ def __init__(self, service_accounts: ServiceAccounts) -> None: self.retrieve = _legacy_response.to_raw_response_wrapper( service_accounts.retrieve, ) + self.update = _legacy_response.to_raw_response_wrapper( + service_accounts.update, + ) self.list = _legacy_response.to_raw_response_wrapper( service_accounts.list, ) @@ -468,6 +591,9 @@ def __init__(self, service_accounts: AsyncServiceAccounts) -> None: self.retrieve = _legacy_response.async_to_raw_response_wrapper( service_accounts.retrieve, ) + self.update = _legacy_response.async_to_raw_response_wrapper( + service_accounts.update, + ) self.list = _legacy_response.async_to_raw_response_wrapper( service_accounts.list, ) @@ -486,6 +612,9 @@ def __init__(self, service_accounts: ServiceAccounts) -> None: self.retrieve = to_streamed_response_wrapper( service_accounts.retrieve, ) + self.update = to_streamed_response_wrapper( + service_accounts.update, + ) self.list = to_streamed_response_wrapper( service_accounts.list, ) @@ -504,6 +633,9 @@ def __init__(self, service_accounts: AsyncServiceAccounts) -> None: self.retrieve = async_to_streamed_response_wrapper( service_accounts.retrieve, ) + self.update = async_to_streamed_response_wrapper( + service_accounts.update, + ) self.list = async_to_streamed_response_wrapper( service_accounts.list, ) diff --git a/src/openai/resources/admin/organization/projects/spend_alerts.py b/src/openai/resources/admin/organization/projects/spend_alerts.py new file mode 100644 index 0000000000..3bc5d0c1f9 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/spend_alerts.py @@ -0,0 +1,589 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ....._base_client import AsyncPaginator, make_request_options +from .....types.admin.organization.projects import ( + spend_alert_list_params, + spend_alert_create_params, + spend_alert_update_params, +) +from .....types.admin.organization.projects.project_spend_alert import ProjectSpendAlert +from .....types.admin.organization.projects.project_spend_alert_deleted import ProjectSpendAlertDeleted + +__all__ = ["SpendAlerts", "AsyncSpendAlerts"] + + +class SpendAlerts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendAlertsWithStreamingResponse(self) + + def create( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Creates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def update( + self, + alert_id: str, + *, + project_id: str, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Updates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[ProjectSpendAlert]: + """Lists project spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + page=SyncConversationCursorPage[ProjectSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectSpendAlert, + ) + + def delete( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlertDeleted: + """ + Deletes a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._delete( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlertDeleted, + ) + + +class AsyncSpendAlerts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendAlertsWithStreamingResponse(self) + + async def create( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Creates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + async def update( + self, + alert_id: str, + *, + project_id: str, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Updates a project spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + + def list( + self, + project_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ProjectSpendAlert, AsyncConversationCursorPage[ProjectSpendAlert]]: + """Lists project spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get_api_list( + path_template("/organization/projects/{project_id}/spend_alerts", project_id=project_id), + page=AsyncConversationCursorPage[ProjectSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=ProjectSpendAlert, + ) + + async def delete( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlertDeleted: + """ + Deletes a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._delete( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlertDeleted, + ) + + +class SpendAlertsWithRawResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithRawResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class SpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = to_streamed_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = async_to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = async_to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = async_to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = async_to_streamed_response_wrapper( + spend_alerts.delete, + ) diff --git a/src/openai/resources/admin/organization/projects/users/roles.py b/src/openai/resources/admin/organization/projects/users/roles.py index 6a3233e275..38b79acec3 100644 --- a/src/openai/resources/admin/organization/projects/users/roles.py +++ b/src/openai/resources/admin/organization/projects/users/roles.py @@ -18,6 +18,7 @@ from ......types.admin.organization.projects.users.role_list_response import RoleListResponse from ......types.admin.organization.projects.users.role_create_response import RoleCreateResponse from ......types.admin.organization.projects.users.role_delete_response import RoleDeleteResponse +from ......types.admin.organization.projects.users.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -86,6 +87,54 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -258,6 +307,54 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + project_id: str, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves a project role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template( + "/projects/{project_id}/users/{user_id}/roles/{role_id}", + project_id=project_id, + user_id=user_id, + role_id=role_id, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -373,6 +470,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -388,6 +488,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -403,6 +506,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -418,6 +524,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/resources/admin/organization/roles.py b/src/openai/resources/admin/organization/roles.py index b25bbfb21e..056a47d7b2 100644 --- a/src/openai/resources/admin/organization/roles.py +++ b/src/openai/resources/admin/organization/roles.py @@ -93,6 +93,43 @@ def create( cast_to=Role, ) + def retrieve( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves an organization role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + def update( self, role_id: str, @@ -309,6 +346,43 @@ async def create( cast_to=Role, ) + async def retrieve( + self, + role_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Role: + """ + Retrieves an organization role. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/roles/{role_id}", role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=Role, + ) + async def update( self, role_id: str, @@ -461,6 +535,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( roles.update, ) @@ -479,6 +556,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( roles.update, ) @@ -497,6 +577,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.update = to_streamed_response_wrapper( roles.update, ) @@ -515,6 +598,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.update = async_to_streamed_response_wrapper( roles.update, ) diff --git a/src/openai/resources/admin/organization/spend_alerts.py b/src/openai/resources/admin/organization/spend_alerts.py new file mode 100644 index 0000000000..4f3e223269 --- /dev/null +++ b/src/openai/resources/admin/organization/spend_alerts.py @@ -0,0 +1,553 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import path_template, maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.admin.organization import spend_alert_list_params, spend_alert_create_params, spend_alert_update_params +from ....types.admin.organization.organization_spend_alert import OrganizationSpendAlert +from ....types.admin.organization.organization_spend_alert_deleted import OrganizationSpendAlertDeleted + +__all__ = ["SpendAlerts", "AsyncSpendAlerts"] + + +class SpendAlerts(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendAlertsWithStreamingResponse(self) + + def create( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Creates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/spend_alerts", + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def update( + self, + alert_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Updates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._post( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncConversationCursorPage[OrganizationSpendAlert]: + """Lists organization spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/spend_alerts", + page=SyncConversationCursorPage[OrganizationSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationSpendAlert, + ) + + def delete( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlertDeleted: + """ + Deletes an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._delete( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlertDeleted, + ) + + +class AsyncSpendAlerts(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendAlertsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendAlertsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendAlertsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendAlertsWithStreamingResponse(self) + + async def create( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_create_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Creates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/spend_alerts", + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_create_params.SpendAlertCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + async def update( + self, + alert_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + notification_channel: spend_alert_update_params.NotificationChannel, + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Updates an organization spend alert. + + Args: + currency: The currency for the threshold amount. + + interval: The time interval for evaluating spend against the threshold. + + notification_channel: Email notification settings for a spend alert. + + threshold_amount: The alert threshold amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._post( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "notification_channel": notification_channel, + "threshold_amount": threshold_amount, + }, + spend_alert_update_params.SpendAlertUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + + def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[OrganizationSpendAlert, AsyncConversationCursorPage[OrganizationSpendAlert]]: + """Lists organization spend alerts. + + Args: + after: Cursor for pagination. + + Provide the ID of the last spend alert from the previous + response to fetch the next page. + + before: Cursor for pagination. Provide the ID of the first spend alert from the previous + response to fetch the previous page. + + limit: A limit on the number of spend alerts to return. Defaults to 20. + + order: Sort order for the returned spend alerts. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/organization/spend_alerts", + page=AsyncConversationCursorPage[OrganizationSpendAlert], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "before": before, + "limit": limit, + "order": order, + }, + spend_alert_list_params.SpendAlertListParams, + ), + security={"admin_api_key_auth": True}, + ), + model=OrganizationSpendAlert, + ) + + async def delete( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlertDeleted: + """ + Deletes an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._delete( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlertDeleted, + ) + + +class SpendAlertsWithRawResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithRawResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.create, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.update, + ) + self.list = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.list, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.delete, + ) + + +class SpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: SpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = to_streamed_response_wrapper( + spend_alerts.delete, + ) + + +class AsyncSpendAlertsWithStreamingResponse: + def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: + self._spend_alerts = spend_alerts + + self.create = async_to_streamed_response_wrapper( + spend_alerts.create, + ) + self.update = async_to_streamed_response_wrapper( + spend_alerts.update, + ) + self.list = async_to_streamed_response_wrapper( + spend_alerts.list, + ) + self.delete = async_to_streamed_response_wrapper( + spend_alerts.delete, + ) diff --git a/src/openai/resources/admin/organization/users/roles.py b/src/openai/resources/admin/organization/users/roles.py index 01ea5f4844..6a20ab52ff 100644 --- a/src/openai/resources/admin/organization/users/roles.py +++ b/src/openai/resources/admin/organization/users/roles.py @@ -18,6 +18,7 @@ from .....types.admin.organization.users.role_list_response import RoleListResponse from .....types.admin.organization.users.role_create_response import RoleCreateResponse from .....types.admin.organization.users.role_delete_response import RoleDeleteResponse +from .....types.admin.organization.users.role_retrieve_response import RoleRetrieveResponse __all__ = ["Roles", "AsyncRoles"] @@ -83,6 +84,46 @@ def create( cast_to=RoleCreateResponse, ) + def retrieve( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return self._get( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -241,6 +282,46 @@ async def create( cast_to=RoleCreateResponse, ) + async def retrieve( + self, + role_id: str, + *, + user_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RoleRetrieveResponse: + """ + Retrieves an organization role assigned to a user. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not user_id: + raise ValueError(f"Expected a non-empty value for `user_id` but received {user_id!r}") + if not role_id: + raise ValueError(f"Expected a non-empty value for `role_id` but received {role_id!r}") + return await self._get( + path_template("/organization/users/{user_id}/roles/{role_id}", user_id=user_id, role_id=role_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=RoleRetrieveResponse, + ) + def list( self, user_id: str, @@ -345,6 +426,9 @@ def __init__(self, roles: Roles) -> None: self.create = _legacy_response.to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.to_raw_response_wrapper( roles.list, ) @@ -360,6 +444,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( roles.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + roles.retrieve, + ) self.list = _legacy_response.async_to_raw_response_wrapper( roles.list, ) @@ -375,6 +462,9 @@ def __init__(self, roles: Roles) -> None: self.create = to_streamed_response_wrapper( roles.create, ) + self.retrieve = to_streamed_response_wrapper( + roles.retrieve, + ) self.list = to_streamed_response_wrapper( roles.list, ) @@ -390,6 +480,9 @@ def __init__(self, roles: AsyncRoles) -> None: self.create = async_to_streamed_response_wrapper( roles.create, ) + self.retrieve = async_to_streamed_response_wrapper( + roles.retrieve, + ) self.list = async_to_streamed_response_wrapper( roles.list, ) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index ebb62b0eb8..d14d8dce41 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -34,13 +34,17 @@ from .invite_delete_response import InviteDeleteResponse as InviteDeleteResponse from .audit_log_list_response import AuditLogListResponse as AuditLogListResponse from .certificate_list_params import CertificateListParams as CertificateListParams +from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams from .usage_embeddings_params import UsageEmbeddingsParams as UsageEmbeddingsParams +from .organization_spend_alert import OrganizationSpendAlert as OrganizationSpendAlert from .usage_completions_params import UsageCompletionsParams as UsageCompletionsParams from .usage_moderations_params import UsageModerationsParams as UsageModerationsParams from .admin_api_key_list_params import AdminAPIKeyListParams as AdminAPIKeyListParams from .certificate_create_params import CertificateCreateParams as CertificateCreateParams from .certificate_list_response import CertificateListResponse as CertificateListResponse from .certificate_update_params import CertificateUpdateParams as CertificateUpdateParams +from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams +from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams from .usage_embeddings_response import UsageEmbeddingsResponse as UsageEmbeddingsResponse from .usage_completions_response import UsageCompletionsResponse as UsageCompletionsResponse from .usage_moderations_response import UsageModerationsResponse as UsageModerationsResponse @@ -49,7 +53,9 @@ from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams from .certificate_delete_response import CertificateDeleteResponse as CertificateDeleteResponse from .certificate_retrieve_params import CertificateRetrieveParams as CertificateRetrieveParams +from .organization_data_retention import OrganizationDataRetention as OrganizationDataRetention from .usage_audio_speeches_params import UsageAudioSpeechesParams as UsageAudioSpeechesParams +from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams from .usage_vector_stores_response import UsageVectorStoresResponse as UsageVectorStoresResponse from .admin_api_key_create_response import AdminAPIKeyCreateResponse as AdminAPIKeyCreateResponse from .admin_api_key_delete_response import AdminAPIKeyDeleteResponse as AdminAPIKeyDeleteResponse @@ -60,6 +66,7 @@ from .usage_file_search_calls_params import UsageFileSearchCallsParams as UsageFileSearchCallsParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse from .usage_web_search_calls_response import UsageWebSearchCallsResponse as UsageWebSearchCallsResponse +from .organization_spend_alert_deleted import OrganizationSpendAlertDeleted as OrganizationSpendAlertDeleted from .usage_file_search_calls_response import UsageFileSearchCallsResponse as UsageFileSearchCallsResponse from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse diff --git a/src/openai/types/admin/organization/data_retention_update_params.py b/src/openai/types/admin/organization/data_retention_update_params.py new file mode 100644 index 0000000000..b6510e7955 --- /dev/null +++ b/src/openai/types/admin/organization/data_retention_update_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["DataRetentionUpdateParams"] + + +class DataRetentionUpdateParams(TypedDict, total=False): + retention_type: Required[ + Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + ] + """The desired organization data retention type.""" diff --git a/src/openai/types/admin/organization/group.py b/src/openai/types/admin/organization/group.py index a5823b1442..045980478f 100644 --- a/src/openai/types/admin/organization/group.py +++ b/src/openai/types/admin/organization/group.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing_extensions import Literal + from ...._models import BaseModel __all__ = ["Group"] @@ -14,7 +16,7 @@ class Group(BaseModel): created_at: int """Unix timestamp (in seconds) when the group was created.""" - group_type: str + group_type: Literal["group", "tenant_group"] """The type of the group.""" is_scim_managed: bool diff --git a/src/openai/types/admin/organization/groups/__init__.py b/src/openai/types/admin/organization/groups/__init__.py index 22189c1085..884cc0d92f 100644 --- a/src/openai/types/admin/organization/groups/__init__.py +++ b/src/openai/types/admin/organization/groups/__init__.py @@ -11,4 +11,6 @@ from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse from .user_create_response import UserCreateResponse as UserCreateResponse from .user_delete_response import UserDeleteResponse as UserDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse +from .user_retrieve_response import UserRetrieveResponse as UserRetrieveResponse from .organization_group_user import OrganizationGroupUser as OrganizationGroupUser diff --git a/src/openai/types/admin/organization/groups/role_list_response.py b/src/openai/types/admin/organization/groups/role_list_response.py index 337d517ba1..fc64f8e9a0 100644 --- a/src/openai/types/admin/organization/groups/role_list_response.py +++ b/src/openai/types/admin/organization/groups/role_list_response.py @@ -4,7 +4,13 @@ from ....._models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/groups/role_retrieve_response.py b/src/openai/types/admin/organization/groups/role_retrieve_response.py new file mode 100644 index 0000000000..576010daad --- /dev/null +++ b/src/openai/types/admin/organization/groups/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/groups/user_retrieve_response.py b/src/openai/types/admin/organization/groups/user_retrieve_response.py new file mode 100644 index 0000000000..977db3577f --- /dev/null +++ b/src/openai/types/admin/organization/groups/user_retrieve_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["UserRetrieveResponse"] + + +class UserRetrieveResponse(BaseModel): + """Details about a user returned from an organization group membership lookup.""" + + id: str + """Identifier for the user.""" + + email: Optional[str] = None + """Email address of the user, or `null` for users without an email.""" + + is_service_account: Optional[bool] = None + """Whether the user is a service account.""" + + name: str + """Display name of the user.""" + + picture: Optional[str] = None + """URL of the user's profile picture, if available.""" + + user_type: Literal["user", "tenant_user"] + """The type of user.""" diff --git a/src/openai/types/admin/organization/organization_data_retention.py b/src/openai/types/admin/organization/organization_data_retention.py new file mode 100644 index 0000000000..0022094f69 --- /dev/null +++ b/src/openai/types/admin/organization/organization_data_retention.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationDataRetention"] + + +class OrganizationDataRetention(BaseModel): + """Represents the organization's data retention control setting.""" + + object: Literal["organization.data_retention"] + """The object type, which is always `organization.data_retention`.""" + + type: Literal[ + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + """The configured organization data retention type.""" diff --git a/src/openai/types/admin/organization/organization_spend_alert.py b/src/openai/types/admin/organization/organization_spend_alert.py new file mode 100644 index 0000000000..a541926a01 --- /dev/null +++ b/src/openai/types/admin/organization/organization_spend_alert.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendAlert", "NotificationChannel"] + + +class NotificationChannel(BaseModel): + """Email notification settings for a spend alert.""" + + recipients: List[str] + """Email addresses that receive the spend alert notification.""" + + type: Literal["email"] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] = None + """Optional subject prefix for alert emails.""" + + +class OrganizationSpendAlert(BaseModel): + """Represents a spend alert configured at the organization level.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + currency: Literal["USD"] + """The currency for the threshold amount.""" + + interval: Literal["month"] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: NotificationChannel + """Email notification settings for a spend alert.""" + + object: Literal["organization.spend_alert"] + """The object type, which is always `organization.spend_alert`.""" + + threshold_amount: int + """The alert threshold amount, in cents.""" diff --git a/src/openai/types/admin/organization/organization_spend_alert_deleted.py b/src/openai/types/admin/organization/organization_spend_alert_deleted.py new file mode 100644 index 0000000000..74fab027ee --- /dev/null +++ b/src/openai/types/admin/organization/organization_spend_alert_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendAlertDeleted"] + + +class OrganizationSpendAlertDeleted(BaseModel): + """Confirmation payload returned after deleting an organization spend alert.""" + + id: str + """The deleted spend alert ID.""" + + deleted: bool + """Whether the spend alert was deleted.""" + + object: Literal["organization.spend_alert.deleted"] + """Always `organization.spend_alert.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py index 1b78bae7a8..1c510be58d 100644 --- a/src/openai/types/admin/organization/projects/__init__.py +++ b/src/openai/types/admin/organization/projects/__init__.py @@ -15,19 +15,28 @@ from .user_update_params import UserUpdateParams as UserUpdateParams from .api_key_list_params import APIKeyListParams as APIKeyListParams from .group_create_params import GroupCreateParams as GroupCreateParams +from .project_spend_alert import ProjectSpendAlert as ProjectSpendAlert from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse from .user_delete_response import UserDeleteResponse as UserDeleteResponse from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse +from .group_retrieve_params import GroupRetrieveParams as GroupRetrieveParams +from .project_data_retention import ProjectDataRetention as ProjectDataRetention from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse from .certificate_list_params import CertificateListParams as CertificateListParams from .project_service_account import ProjectServiceAccount as ProjectServiceAccount +from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams from .certificate_list_response import CertificateListResponse as CertificateListResponse from .project_model_permissions import ProjectModelPermissions as ProjectModelPermissions +from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams +from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams +from .project_spend_alert_deleted import ProjectSpendAlertDeleted as ProjectSpendAlertDeleted from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams +from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams from .service_account_create_params import ServiceAccountCreateParams as ServiceAccountCreateParams +from .service_account_update_params import ServiceAccountUpdateParams as ServiceAccountUpdateParams from .model_permission_update_params import ModelPermissionUpdateParams as ModelPermissionUpdateParams from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse from .project_hosted_tool_permissions import ProjectHostedToolPermissions as ProjectHostedToolPermissions diff --git a/src/openai/types/admin/organization/projects/data_retention_update_params.py b/src/openai/types/admin/organization/projects/data_retention_update_params.py new file mode 100644 index 0000000000..e7291d207a --- /dev/null +++ b/src/openai/types/admin/organization/projects/data_retention_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["DataRetentionUpdateParams"] + + +class DataRetentionUpdateParams(TypedDict, total=False): + retention_type: Required[ + Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + ] + """The desired project data retention type.""" diff --git a/src/openai/types/admin/organization/projects/group_retrieve_params.py b/src/openai/types/admin/organization/projects/group_retrieve_params.py new file mode 100644 index 0000000000..084f345f43 --- /dev/null +++ b/src/openai/types/admin/organization/projects/group_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["GroupRetrieveParams"] + + +class GroupRetrieveParams(TypedDict, total=False): + project_id: Required[str] + + group_type: Literal["group", "tenant_group"] + """The type of group to retrieve.""" diff --git a/src/openai/types/admin/organization/projects/groups/__init__.py b/src/openai/types/admin/organization/projects/groups/__init__.py index ed464fde83..5e67517593 100644 --- a/src/openai/types/admin/organization/projects/groups/__init__.py +++ b/src/openai/types/admin/organization/projects/groups/__init__.py @@ -7,3 +7,4 @@ from .role_list_response import RoleListResponse as RoleListResponse from .role_create_response import RoleCreateResponse as RoleCreateResponse from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/src/openai/types/admin/organization/projects/groups/role_list_response.py b/src/openai/types/admin/organization/projects/groups/role_list_response.py index 72934bde13..d43d0f806b 100644 --- a/src/openai/types/admin/organization/projects/groups/role_list_response.py +++ b/src/openai/types/admin/organization/projects/groups/role_list_response.py @@ -4,7 +4,13 @@ from ......_models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py b/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py new file mode 100644 index 0000000000..0c10cf0092 --- /dev/null +++ b/src/openai/types/admin/organization/projects/groups/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/projects/project_data_retention.py b/src/openai/types/admin/organization/projects/project_data_retention.py new file mode 100644 index 0000000000..30329e60f4 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_data_retention.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectDataRetention"] + + +class ProjectDataRetention(BaseModel): + """Represents a project's data retention control setting.""" + + object: Literal["project.data_retention"] + """The object type, which is always `project.data_retention`.""" + + type: Literal[ + "organization_default", + "none", + "zero_data_retention", + "modified_abuse_monitoring", + "enhanced_zero_data_retention", + "enhanced_modified_abuse_monitoring", + ] + """The configured project data retention type.""" diff --git a/src/openai/types/admin/organization/projects/project_group.py b/src/openai/types/admin/organization/projects/project_group.py index b3da08ca32..4efb29f91e 100644 --- a/src/openai/types/admin/organization/projects/project_group.py +++ b/src/openai/types/admin/organization/projects/project_group.py @@ -19,7 +19,7 @@ class ProjectGroup(BaseModel): group_name: str """Display name of the group.""" - group_type: str + group_type: Literal["group", "tenant_group"] """The type of the group.""" object: Literal["project.group"] diff --git a/src/openai/types/admin/organization/projects/project_spend_alert.py b/src/openai/types/admin/organization/projects/project_spend_alert.py new file mode 100644 index 0000000000..269ea54db5 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_spend_alert.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendAlert", "NotificationChannel"] + + +class NotificationChannel(BaseModel): + """Email notification settings for a spend alert.""" + + recipients: List[str] + """Email addresses that receive the spend alert notification.""" + + type: Literal["email"] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] = None + """Optional subject prefix for alert emails.""" + + +class ProjectSpendAlert(BaseModel): + """Represents a spend alert configured at the project level.""" + + id: str + """The identifier, which can be referenced in API endpoints.""" + + currency: Literal["USD"] + """The currency for the threshold amount.""" + + interval: Literal["month"] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: NotificationChannel + """Email notification settings for a spend alert.""" + + object: Literal["project.spend_alert"] + """The object type, which is always `project.spend_alert`.""" + + threshold_amount: int + """The alert threshold amount, in cents.""" diff --git a/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py b/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py new file mode 100644 index 0000000000..68be581307 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_spend_alert_deleted.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendAlertDeleted"] + + +class ProjectSpendAlertDeleted(BaseModel): + """Confirmation payload returned after deleting a project spend alert.""" + + id: str + """The deleted spend alert ID.""" + + deleted: bool + """Whether the spend alert was deleted.""" + + object: Literal["project.spend_alert.deleted"] + """Always `project.spend_alert.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/service_account_update_params.py b/src/openai/types/admin/organization/projects/service_account_update_params.py new file mode 100644 index 0000000000..852e5d5a5a --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_account_update_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ServiceAccountUpdateParams"] + + +class ServiceAccountUpdateParams(TypedDict, total=False): + project_id: Required[str] + + name: str + """The updated service account name.""" + + role: Literal["member", "owner"] + """The updated service account role.""" diff --git a/src/openai/types/admin/organization/projects/spend_alert_create_params.py b/src/openai/types/admin/organization/projects/spend_alert_create_params.py new file mode 100644 index 0000000000..74914d8646 --- /dev/null +++ b/src/openai/types/admin/organization/projects/spend_alert_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["SpendAlertCreateParams", "NotificationChannel"] + + +class SpendAlertCreateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/projects/spend_alert_list_params.py b/src/openai/types/admin/organization/projects/spend_alert_list_params.py new file mode 100644 index 0000000000..a37bad5879 --- /dev/null +++ b/src/openai/types/admin/organization/projects/spend_alert_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SpendAlertListParams"] + + +class SpendAlertListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last spend alert from the previous response to fetch the + next page. + """ + + before: str + """Cursor for pagination. + + Provide the ID of the first spend alert from the previous response to fetch the + previous page. + """ + + limit: int + """A limit on the number of spend alerts to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned spend alerts.""" diff --git a/src/openai/types/admin/organization/projects/spend_alert_update_params.py b/src/openai/types/admin/organization/projects/spend_alert_update_params.py new file mode 100644 index 0000000000..1611c531e8 --- /dev/null +++ b/src/openai/types/admin/organization/projects/spend_alert_update_params.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ....._types import SequenceNotStr + +__all__ = ["SpendAlertUpdateParams", "NotificationChannel"] + + +class SpendAlertUpdateParams(TypedDict, total=False): + project_id: Required[str] + + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/projects/users/__init__.py b/src/openai/types/admin/organization/projects/users/__init__.py index ed464fde83..5e67517593 100644 --- a/src/openai/types/admin/organization/projects/users/__init__.py +++ b/src/openai/types/admin/organization/projects/users/__init__.py @@ -7,3 +7,4 @@ from .role_list_response import RoleListResponse as RoleListResponse from .role_create_response import RoleCreateResponse as RoleCreateResponse from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/src/openai/types/admin/organization/projects/users/role_list_response.py b/src/openai/types/admin/organization/projects/users/role_list_response.py index 72934bde13..d43d0f806b 100644 --- a/src/openai/types/admin/organization/projects/users/role_list_response.py +++ b/src/openai/types/admin/organization/projects/users/role_list_response.py @@ -4,7 +4,13 @@ from ......_models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/projects/users/role_retrieve_response.py b/src/openai/types/admin/organization/projects/users/role_retrieve_response.py new file mode 100644 index 0000000000..0c10cf0092 --- /dev/null +++ b/src/openai/types/admin/organization/projects/users/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ......_models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/admin/organization/spend_alert_create_params.py b/src/openai/types/admin/organization/spend_alert_create_params.py new file mode 100644 index 0000000000..f787abdfe1 --- /dev/null +++ b/src/openai/types/admin/organization/spend_alert_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["SpendAlertCreateParams", "NotificationChannel"] + + +class SpendAlertCreateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/spend_alert_list_params.py b/src/openai/types/admin/organization/spend_alert_list_params.py new file mode 100644 index 0000000000..a37bad5879 --- /dev/null +++ b/src/openai/types/admin/organization/spend_alert_list_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypedDict + +__all__ = ["SpendAlertListParams"] + + +class SpendAlertListParams(TypedDict, total=False): + after: str + """Cursor for pagination. + + Provide the ID of the last spend alert from the previous response to fetch the + next page. + """ + + before: str + """Cursor for pagination. + + Provide the ID of the first spend alert from the previous response to fetch the + previous page. + """ + + limit: int + """A limit on the number of spend alerts to return. Defaults to 20.""" + + order: Literal["asc", "desc"] + """Sort order for the returned spend alerts.""" diff --git a/src/openai/types/admin/organization/spend_alert_update_params.py b/src/openai/types/admin/organization/spend_alert_update_params.py new file mode 100644 index 0000000000..ddba8a81e1 --- /dev/null +++ b/src/openai/types/admin/organization/spend_alert_update_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ...._types import SequenceNotStr + +__all__ = ["SpendAlertUpdateParams", "NotificationChannel"] + + +class SpendAlertUpdateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold.""" + + notification_channel: Required[NotificationChannel] + """Email notification settings for a spend alert.""" + + threshold_amount: Required[int] + """The alert threshold amount, in cents.""" + + +class NotificationChannel(TypedDict, total=False): + """Email notification settings for a spend alert.""" + + recipients: Required[SequenceNotStr[str]] + """Email addresses that receive the spend alert notification.""" + + type: Required[Literal["email"]] + """The notification channel type. Currently only `email` is supported.""" + + subject_prefix: Optional[str] + """Optional subject prefix for alert emails.""" diff --git a/src/openai/types/admin/organization/users/__init__.py b/src/openai/types/admin/organization/users/__init__.py index ed464fde83..5e67517593 100644 --- a/src/openai/types/admin/organization/users/__init__.py +++ b/src/openai/types/admin/organization/users/__init__.py @@ -7,3 +7,4 @@ from .role_list_response import RoleListResponse as RoleListResponse from .role_create_response import RoleCreateResponse as RoleCreateResponse from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse +from .role_retrieve_response import RoleRetrieveResponse as RoleRetrieveResponse diff --git a/src/openai/types/admin/organization/users/role_list_response.py b/src/openai/types/admin/organization/users/role_list_response.py index 337d517ba1..fc64f8e9a0 100644 --- a/src/openai/types/admin/organization/users/role_list_response.py +++ b/src/openai/types/admin/organization/users/role_list_response.py @@ -4,7 +4,13 @@ from ....._models import BaseModel -__all__ = ["RoleListResponse"] +__all__ = ["RoleListResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str class RoleListResponse(BaseModel): @@ -15,6 +21,9 @@ class RoleListResponse(BaseModel): id: str """Identifier for the role.""" + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + created_at: Optional[int] = None """When the role was created.""" diff --git a/src/openai/types/admin/organization/users/role_retrieve_response.py b/src/openai/types/admin/organization/users/role_retrieve_response.py new file mode 100644 index 0000000000..576010daad --- /dev/null +++ b/src/openai/types/admin/organization/users/role_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ....._models import BaseModel + +__all__ = ["RoleRetrieveResponse", "AssignmentSource"] + + +class AssignmentSource(BaseModel): + principal_id: str + + principal_type: str + + +class RoleRetrieveResponse(BaseModel): + """ + Detailed information about a role assignment entry returned when listing assignments. + """ + + id: str + """Identifier for the role.""" + + assignment_sources: Optional[List[AssignmentSource]] = None + """Principals from which the role assignment is inherited, when available.""" + + created_at: Optional[int] = None + """When the role was created.""" + + created_by: Optional[str] = None + """Identifier of the actor who created the role.""" + + created_by_user_obj: Optional[Dict[str, object]] = None + """User details for the actor that created the role, when available.""" + + description: Optional[str] = None + """Description of the role.""" + + metadata: Optional[Dict[str, object]] = None + """Arbitrary metadata stored on the role.""" + + name: str + """Name of the role.""" + + permissions: List[str] + """Permissions associated with the role.""" + + predefined_role: bool + """Whether the role is predefined by OpenAI.""" + + resource_type: str + """Resource type the role applies to.""" + + updated_at: Optional[int] = None + """When the role was last updated.""" diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index d3d4afbee3..de6001e146 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -46,7 +46,7 @@ class ActionOpenPage(BaseModel): """Action type "open_page" - Opens a specific URL from search results.""" type: Literal["open_page"] - """The action type. Always `open_page`.""" + """The action type.""" url: Optional[str] = None """The URL opened by the model.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index e0d50757a3..15e313b0d3 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -47,7 +47,7 @@ class ActionOpenPage(TypedDict, total=False): """Action type "open_page" - Opens a specific URL from search results.""" type: Required[Literal["open_page"]] - """The action type. Always `open_page`.""" + """The action type.""" url: Optional[str] """The URL opened by the model.""" diff --git a/tests/api_resources/admin/organization/groups/test_roles.py b/tests/api_resources/admin/organization/groups/test_roles.py index 08702fddd2..6f2235a468 100644 --- a/tests/api_resources/admin/organization/groups/test_roles.py +++ b/tests/api_resources/admin/organization/groups/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -64,6 +65,54 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.groups.roles.retrieve( + role_id="role_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="", + group_id="group_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.groups.roles.list( @@ -208,6 +257,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.groups.roles.retrieve( + role_id="role_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="role_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.groups.roles.with_raw_response.retrieve( + role_id="", + group_id="group_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.groups.roles.list( diff --git a/tests/api_resources/admin/organization/groups/test_users.py b/tests/api_resources/admin/organization/groups/test_users.py index eda6be6bbf..5003db2ad1 100644 --- a/tests/api_resources/admin/organization/groups/test_users.py +++ b/tests/api_resources/admin/organization/groups/test_users.py @@ -13,6 +13,7 @@ from openai.types.admin.organization.groups import ( UserCreateResponse, UserDeleteResponse, + UserRetrieveResponse, OrganizationGroupUser, ) @@ -64,6 +65,54 @@ def test_path_params_create(self, client: OpenAI) -> None: user_id="user_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + user = client.admin.organization.groups.users.retrieve( + user_id="user_id", + group_id="group_id", + ) + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.groups.users.with_streaming_response.retrieve( + user_id="user_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="", + group_id="group_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: user = client.admin.organization.groups.users.list( @@ -208,6 +257,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: user_id="user_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + user = await async_client.admin.organization.groups.users.retrieve( + user_id="user_id", + group_id="group_id", + ) + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + user = response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.users.with_streaming_response.retrieve( + user_id="user_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + user = await response.parse() + assert_matches_type(UserRetrieveResponse, user, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="user_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.groups.users.with_raw_response.retrieve( + user_id="", + group_id="group_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: user = await async_client.admin.organization.groups.users.list( diff --git a/tests/api_resources/admin/organization/projects/groups/test_roles.py b/tests/api_resources/admin/organization/projects/groups/test_roles.py index a129142ea9..4e62facc55 100644 --- a/tests/api_resources/admin/organization/projects/groups/test_roles.py +++ b/tests/api_resources/admin/organization/projects/groups/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -75,6 +76,66 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.projects.groups.roles.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + group_id="group_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + group_id="group_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.projects.groups.roles.list( @@ -253,6 +314,66 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.groups.roles.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + group_id="group_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + group_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.groups.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + group_id="group_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.projects.groups.roles.list( diff --git a/tests/api_resources/admin/organization/projects/test_data_retention.py b/tests/api_resources/admin/organization/projects/test_data_retention.py new file mode 100644 index 0000000000..d640e78e8d --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_data_retention.py @@ -0,0 +1,184 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects import ProjectDataRetention + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDataRetention: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + data_retention = client.admin.organization.projects.data_retention.retrieve( + "project_id", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.data_retention.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + data_retention = client.admin.organization.projects.data_retention.update( + project_id="project_id", + retention_type="organization_default", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="project_id", + retention_type="organization_default", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.data_retention.with_streaming_response.update( + project_id="project_id", + retention_type="organization_default", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="", + retention_type="organization_default", + ) + + +class TestAsyncDataRetention: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.projects.data_retention.retrieve( + "project_id", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.data_retention.with_streaming_response.retrieve( + "project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.data_retention.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.projects.data_retention.update( + project_id="project_id", + retention_type="organization_default", + ) + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="project_id", + retention_type="organization_default", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.data_retention.with_streaming_response.update( + project_id="project_id", + retention_type="organization_default", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(ProjectDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.data_retention.with_raw_response.update( + project_id="", + retention_type="organization_default", + ) diff --git a/tests/api_resources/admin/organization/projects/test_groups.py b/tests/api_resources/admin/organization/projects/test_groups.py index 2db448e9b2..46a68076df 100644 --- a/tests/api_resources/admin/organization/projects/test_groups.py +++ b/tests/api_resources/admin/organization/projects/test_groups.py @@ -67,6 +67,63 @@ def test_path_params_create(self, client: OpenAI) -> None: role="role", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: OpenAI) -> None: + group = client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + group_type="group", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.groups.with_streaming_response.retrieve( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="", + project_id="project_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: group = client.admin.organization.projects.groups.list( @@ -215,6 +272,63 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role="role", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.projects.groups.retrieve( + group_id="group_id", + project_id="project_id", + group_type="group", + ) + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.groups.with_streaming_response.retrieve( + group_id="group_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(ProjectGroup, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="group_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.projects.groups.with_raw_response.retrieve( + group_id="", + project_id="project_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: group = await async_client.admin.organization.projects.groups.list( diff --git a/tests/api_resources/admin/organization/projects/test_roles.py b/tests/api_resources/admin/organization/projects/test_roles.py index 8c78ea21b4..862ea1f412 100644 --- a/tests/api_resources/admin/organization/projects/test_roles.py +++ b/tests/api_resources/admin/organization/projects/test_roles.py @@ -77,6 +77,54 @@ def test_path_params_create(self, client: OpenAI) -> None: role_name="role_name", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.projects.roles.retrieve( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: role = client.admin.organization.projects.roles.update( @@ -294,6 +342,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_name="role_name", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.roles.retrieve( + role_id="role_id", + project_id="project_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.projects.roles.update( diff --git a/tests/api_resources/admin/organization/projects/test_service_accounts.py b/tests/api_resources/admin/organization/projects/test_service_accounts.py index 7c94283323..e8e68596a2 100644 --- a/tests/api_resources/admin/organization/projects/test_service_accounts.py +++ b/tests/api_resources/admin/organization/projects/test_service_accounts.py @@ -112,6 +112,64 @@ def test_path_params_retrieve(self, client: OpenAI) -> None: project_id="project_id", ) + @parametrize + def test_method_update(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + name="name", + role="member", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.service_accounts.with_streaming_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="", + project_id="project_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: service_account = client.admin.organization.projects.service_accounts.list( @@ -303,6 +361,64 @@ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: project_id="project_id", ) + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.update( + service_account_id="service_account_id", + project_id="project_id", + name="name", + role="member", + ) + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + service_account = response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.service_accounts.with_streaming_response.update( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + service_account = await response.parse() + assert_matches_type(ProjectServiceAccount, service_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.with_raw_response.update( + service_account_id="", + project_id="project_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: service_account = await async_client.admin.organization.projects.service_accounts.list( diff --git a/tests/api_resources/admin/organization/projects/test_spend_alerts.py b/tests/api_resources/admin/organization/projects/test_spend_alerts.py new file mode 100644 index 0000000000..c763401af3 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_spend_alerts.py @@ -0,0 +1,582 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization.projects import ( + ProjectSpendAlert, + ProjectSpendAlertDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSpendAlerts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + ) + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="", + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.delete( + alert_id="alert_id", + project_id="project_id", + ) + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.delete( + alert_id="alert_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="", + project_id="project_id", + ) + + +class TestAsyncSpendAlerts: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.create( + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.create( + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="alert_id", + project_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.update( + alert_id="", + project_id="project_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.list( + project_id="project_id", + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.list( + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(AsyncConversationCursorPage[ProjectSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.list( + project_id="", + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.delete( + alert_id="alert_id", + project_id="project_id", + ) + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.delete( + alert_id="alert_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(ProjectSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="alert_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.delete( + alert_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/users/test_roles.py b/tests/api_resources/admin/organization/projects/users/test_roles.py index 99c52d494c..5212e5a59c 100644 --- a/tests/api_resources/admin/organization/projects/users/test_roles.py +++ b/tests/api_resources/admin/organization/projects/users/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -75,6 +76,66 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.projects.users.roles.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.users.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + user_id="user_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + user_id="user_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.projects.users.roles.list( @@ -253,6 +314,66 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.projects.users.roles.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.users.roles.with_streaming_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="", + user_id="user_id", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="role_id", + project_id="project_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.projects.users.roles.with_raw_response.retrieve( + role_id="", + project_id="project_id", + user_id="user_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.projects.users.roles.list( diff --git a/tests/api_resources/admin/organization/test_data_retention.py b/tests/api_resources/admin/organization/test_data_retention.py new file mode 100644 index 0000000000..8d943832e6 --- /dev/null +++ b/tests/api_resources/admin/organization/test_data_retention.py @@ -0,0 +1,136 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization import OrganizationDataRetention + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDataRetention: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + data_retention = client.admin.organization.data_retention.retrieve() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.data_retention.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.data_retention.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + data_retention = client.admin.organization.data_retention.update( + retention_type="zero_data_retention", + ) + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.data_retention.with_raw_response.update( + retention_type="zero_data_retention", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.data_retention.with_streaming_response.update( + retention_type="zero_data_retention", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDataRetention: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.data_retention.retrieve() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.data_retention.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.data_retention.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + data_retention = await async_client.admin.organization.data_retention.update( + retention_type="zero_data_retention", + ) + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.data_retention.with_raw_response.update( + retention_type="zero_data_retention", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + data_retention = response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.data_retention.with_streaming_response.update( + retention_type="zero_data_retention", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + data_retention = await response.parse() + assert_matches_type(OrganizationDataRetention, data_retention, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/admin/organization/test_groups.py b/tests/api_resources/admin/organization/test_groups.py index 0eca89f06c..d97ddf579e 100644 --- a/tests/api_resources/admin/organization/test_groups.py +++ b/tests/api_resources/admin/organization/test_groups.py @@ -53,6 +53,44 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + group = client.admin.organization.groups.retrieve( + "group_id", + ) + assert_matches_type(Group, group, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.groups.with_raw_response.retrieve( + "group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.groups.with_streaming_response.retrieve( + "group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.admin.organization.groups.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: group = client.admin.organization.groups.update( @@ -204,6 +242,44 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + group = await async_client.admin.organization.groups.retrieve( + "group_id", + ) + assert_matches_type(Group, group, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.groups.with_raw_response.retrieve( + "group_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + group = response.parse() + assert_matches_type(Group, group, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.groups.with_streaming_response.retrieve( + "group_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + group = await response.parse() + assert_matches_type(Group, group, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.admin.organization.groups.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: group = await async_client.admin.organization.groups.update( diff --git a/tests/api_resources/admin/organization/test_roles.py b/tests/api_resources/admin/organization/test_roles.py index ee70020c23..fbb8515f1c 100644 --- a/tests/api_resources/admin/organization/test_roles.py +++ b/tests/api_resources/admin/organization/test_roles.py @@ -64,6 +64,44 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.roles.retrieve( + "role_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.roles.with_raw_response.retrieve( + "role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.roles.with_streaming_response.retrieve( + "role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.roles.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: role = client.admin.organization.roles.update( @@ -233,6 +271,44 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.roles.retrieve( + "role_id", + ) + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.roles.with_raw_response.retrieve( + "role_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(Role, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.roles.with_streaming_response.retrieve( + "role_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(Role, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.roles.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.roles.update( diff --git a/tests/api_resources/admin/organization/test_spend_alerts.py b/tests/api_resources/admin/organization/test_spend_alerts.py new file mode 100644 index 0000000000..08a3c445c3 --- /dev/null +++ b/tests/api_resources/admin/organization/test_spend_alerts.py @@ -0,0 +1,462 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from openai.types.admin.organization import ( + OrganizationSpendAlert, + OrganizationSpendAlertDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSpendAlerts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.list() + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.list( + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(SyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.delete( + "alert_id", + ) + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.delete( + "alert_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.delete( + "alert_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.spend_alerts.with_raw_response.delete( + "", + ) + + +class TestAsyncSpendAlerts: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.create( + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + "subject_prefix": "subject_prefix", + }, + threshold_amount=0, + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.update( + alert_id="alert_id", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.spend_alerts.with_raw_response.update( + alert_id="", + currency="USD", + interval="month", + notification_channel={ + "recipients": ["string"], + "type": "email", + }, + threshold_amount=0, + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.list() + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.list( + after="after", + before="before", + limit=0, + order="asc", + ) + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(AsyncConversationCursorPage[OrganizationSpendAlert], spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.delete( + "alert_id", + ) + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.delete( + "alert_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.delete( + "alert_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(OrganizationSpendAlertDeleted, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.spend_alerts.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/users/test_roles.py b/tests/api_resources/admin/organization/users/test_roles.py index 2455a38cff..81247b2416 100644 --- a/tests/api_resources/admin/organization/users/test_roles.py +++ b/tests/api_resources/admin/organization/users/test_roles.py @@ -14,6 +14,7 @@ RoleListResponse, RoleCreateResponse, RoleDeleteResponse, + RoleRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -64,6 +65,54 @@ def test_path_params_create(self, client: OpenAI) -> None: role_id="role_id", ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + role = client.admin.organization.users.roles.retrieve( + role_id="role_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.users.roles.with_streaming_response.retrieve( + role_id="role_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="", + user_id="user_id", + ) + @parametrize def test_method_list(self, client: OpenAI) -> None: role = client.admin.organization.users.roles.list( @@ -208,6 +257,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: role_id="role_id", ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + role = await async_client.admin.organization.users.roles.retrieve( + role_id="role_id", + user_id="user_id", + ) + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="user_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + role = response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.users.roles.with_streaming_response.retrieve( + role_id="role_id", + user_id="user_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + role = await response.parse() + assert_matches_type(RoleRetrieveResponse, role, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="role_id", + user_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `role_id` but received ''"): + await async_client.admin.organization.users.roles.with_raw_response.retrieve( + role_id="", + user_id="user_id", + ) + @parametrize async def test_method_list(self, async_client: AsyncOpenAI) -> None: role = await async_client.admin.organization.users.roles.list( From e75766769547601a25ed83b666c4d0fd046881f0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 17:00:15 +0000 Subject: [PATCH 372/408] release: 2.38.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 28c811f943..0e1c697558 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.37.0" + ".": "2.38.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 803d3d3a39..b4f9d758d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 2.38.0 (2026-05-21) + +Full Changelog: [v2.37.0...v2.38.0](https://github.com/openai/openai-python/compare/v2.37.0...v2.38.0) + +### Features + +* **api:** api update ([33d1d01](https://github.com/openai/openai-python/commit/33d1d013250053886a73d178136e6bd1b09df059)) +* **api:** manual updates ([a21700a](https://github.com/openai/openai-python/commit/a21700a2cd510cb9e6c88065ac8e942d4c041aa8)) +* **api:** update OpenAPI spec or Stainless config ([00265c5](https://github.com/openai/openai-python/commit/00265c5daba4d2481452ad35220f1556dab6bcf6)) + + +### Chores + +* **api:** docs updates ([ee10152](https://github.com/openai/openai-python/commit/ee101520d49e22c09cf8096f8cbb3848ea58a1f9)) +* check release PR custom code sync ([2638779](https://github.com/openai/openai-python/commit/2638779a5b8fffaa8fdb6eebc1d734f15d2491f8)) +* remove release automation trigger ([bd6eea5](https://github.com/openai/openai-python/commit/bd6eea559f2996d914258a65e645981bdce3cad4)) +* trigger release automation ([f62d082](https://github.com/openai/openai-python/commit/f62d08201eea8e08d4bb3385662f934d4adccb29)) + ## 2.37.0 (2026-05-13) Full Changelog: [v2.36.0...v2.37.0](https://github.com/openai/openai-python/compare/v2.36.0...v2.37.0) diff --git a/pyproject.toml b/pyproject.toml index 452ac3125a..aedb6d8f51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.37.0" +version = "2.38.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 43d6a12d19..ba9c5f3a3c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.37.0" # x-release-please-version +__version__ = "2.38.0" # x-release-please-version From 8be32d38e1eaf43ee32639eb4f8c9090f4f707f5 Mon Sep 17 00:00:00 2001 From: Jim Blomo Date: Thu, 28 May 2026 16:35:20 -0700 Subject: [PATCH 373/408] [codex] Add Amazon Bedrock provider support --- README.md | 31 +++ examples/bedrock.py | 13 ++ src/openai/__init__.py | 42 +++- src/openai/lib/bedrock.py | 417 ++++++++++++++++++++++++++++++++++ tests/lib/test_bedrock.py | 439 ++++++++++++++++++++++++++++++++++++ tests/test_module_client.py | 64 ++++++ 6 files changed, 1003 insertions(+), 3 deletions(-) create mode 100644 examples/bedrock.py create mode 100644 src/openai/lib/bedrock.py create mode 100644 tests/lib/test_bedrock.py diff --git a/README.md b/README.md index 7af6143c88..6f87246470 100644 --- a/README.md +++ b/README.md @@ -926,6 +926,37 @@ In addition to the options provided in the base `OpenAI` client, the following o 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). +## Amazon Bedrock + +To use this library with [Amazon Bedrock's OpenAI-compatible API](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html), use the `BedrockOpenAI` class instead of the `OpenAI` class. + +```py +from openai import BedrockOpenAI + +# gets the bearer token from AWS_BEARER_TOKEN_BEDROCK and the region from AWS_REGION/AWS_DEFAULT_REGION +client = BedrockOpenAI() + +response = client.responses.create( + model="openai.gpt-5.4", + input="Say hello!", +) + +print(response.output_text) +``` + +`BedrockOpenAI` configures AWS bearer auth and the Bedrock Mantle endpoint, then uses the normal SDK resources. AWS controls which endpoints and features are supported; unsupported calls surface the provider's normal HTTP errors through the SDK. + +Pass `base_url` or set `AWS_BEDROCK_BASE_URL` to override the derived `https://bedrock-mantle..api.aws/openai/v1` endpoint. The legacy module client supports `openai.api_type = "amazon-bedrock"` or `OPENAI_API_TYPE=amazon-bedrock`. + +Set `AWS_BEARER_TOKEN_BEDROCK` to an [Amazon Bedrock API key](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html). To refresh tokens yourself, pass a provider instead of `api_key`: + +```py +client = BedrockOpenAI( + aws_region="us-west-2", + bedrock_token_provider=lambda: refresh_bedrock_token(), +) +``` + ## Versioning 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: diff --git a/examples/bedrock.py b/examples/bedrock.py new file mode 100644 index 0000000000..24dafb5b80 --- /dev/null +++ b/examples/bedrock.py @@ -0,0 +1,13 @@ +from openai import BedrockOpenAI + +client = BedrockOpenAI() + +# For refreshed Bedrock bearer tokens: +# client = BedrockOpenAI(aws_region="us-west-2", bedrock_token_provider=get_bedrock_token) + +response = client.responses.create( + model="openai.gpt-5.4", + input="Say hello!", +) + +print(response.output_text) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index cbaef0615f..8d4265970e 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -79,6 +79,8 @@ "AsyncStream", "OpenAI", "AsyncOpenAI", + "BedrockOpenAI", + "AsyncBedrockOpenAI", "file_from_path", "BaseModel", "DEFAULT_TIMEOUT", @@ -96,9 +98,10 @@ if not _t.TYPE_CHECKING: from ._utils._resources_proxy import resources as resources -from .lib import azure as _azure, pydantic_function_tool as pydantic_function_tool +from .lib import azure as _azure, bedrock as _bedrock, pydantic_function_tool as pydantic_function_tool from .version import VERSION as VERSION from .lib.azure import AzureOpenAI as AzureOpenAI, AsyncAzureOpenAI as AsyncAzureOpenAI +from .lib.bedrock import BedrockOpenAI as BedrockOpenAI, AsyncBedrockOpenAI as AsyncBedrockOpenAI from .lib._old_api import * from .lib.streaming import ( AssistantEventHandler as AssistantEventHandler, @@ -150,7 +153,7 @@ http_client: _httpx.Client | None = None -_ApiType = _te.Literal["openai", "azure"] +_ApiType = _te.Literal["openai", "azure", "amazon-bedrock"] api_type: _ApiType | None = _t.cast(_ApiType, _os.environ.get("OPENAI_API_TYPE")) @@ -162,6 +165,10 @@ azure_ad_token_provider: _azure.AzureADTokenProvider | None = None +_bedrock_api_key: str | None = None + +bedrock_token_provider: _bedrock.BedrockTokenProvider | None = None + class _ModuleClient(OpenAI): # Note: we have to use type: ignores here as overriding class members @@ -294,10 +301,23 @@ class _AzureModuleClient(_ModuleClient, AzureOpenAI): # type: ignore ... +class _BedrockModuleClient(_ModuleClient, BedrockOpenAI): # type: ignore + @property # type: ignore + @override + def api_key(self) -> str | None: + return _bedrock_api_key if _bedrock_api_key is not None else api_key + + @api_key.setter # type: ignore + def api_key(self, value: str | None) -> None: # type: ignore + global _bedrock_api_key + + _bedrock_api_key = value + + class _AmbiguousModuleClientUsageError(OpenAIError): def __init__(self) -> None: super().__init__( - "Ambiguous use of module client; please set `openai.api_type` or the `OPENAI_API_TYPE` environment variable to `openai` or `azure`" + "Ambiguous use of module client; please set `openai.api_type` or the `OPENAI_API_TYPE` environment variable to `openai`, `azure`, or `amazon-bedrock`" ) @@ -370,6 +390,22 @@ def _load_client() -> OpenAI: # type: ignore[reportUnusedFunction] ) return _client + if api_type == "amazon-bedrock": + _client = _BedrockModuleClient( # type: ignore + api_key=api_key, + bedrock_token_provider=bedrock_token_provider, + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + ) + return _client + _client = _ModuleClient( api_key=api_key, admin_api_key=admin_api_key, diff --git a/src/openai/lib/bedrock.py b/src/openai/lib/bedrock.py new file mode 100644 index 0000000000..0a1aec36ee --- /dev/null +++ b/src/openai/lib/bedrock.py @@ -0,0 +1,417 @@ +from __future__ import annotations + +import os +import re +import inspect +from typing import Any, Mapping, Callable, Awaitable, cast +from typing_extensions import Self, override + +import httpx + +from ..auth import WorkloadIdentity +from .._types import NOT_GIVEN, Timeout, NotGiven +from .._utils import is_given +from .._client import OpenAI, AsyncOpenAI +from .._models import SecurityOptions, FinalRequestOptions +from .._exceptions import OpenAIError +from .._base_client import DEFAULT_MAX_RETRIES + +BedrockTokenProvider = Callable[[], str] +AsyncBedrockTokenProvider = Callable[[], "str | Awaitable[str]"] + + +def _normalize_bedrock_base_url(base_url: str | httpx.URL) -> httpx.URL: + """Normalize a Bedrock Responses URL variant back to the provider API root.""" + url = httpx.URL(base_url) + path = url.path.rstrip("/") + responses_match = re.search(r"/responses(?:/.*)?$", path) + if responses_match is not None: + path = path[: responses_match.start()] + + return url.copy_with(path=path or "/") + + +def _resolve_bedrock_base_url(base_url: str | httpx.URL | None, aws_region: str | None) -> httpx.URL: + """Resolve Bedrock base URL precedence from explicit, env, then region config.""" + if isinstance(base_url, str) and not base_url.strip(): + base_url = None + + if base_url is None: + env_base_url = os.environ.get("AWS_BEDROCK_BASE_URL") + if env_base_url is not None and env_base_url.strip(): + base_url = env_base_url + + if base_url is None: + region = aws_region or os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") + if region is None or not region.strip(): + raise OpenAIError( + "Must provide one of the `base_url` or `aws_region` arguments, or set the " + "`AWS_BEDROCK_BASE_URL`, `AWS_REGION`, or `AWS_DEFAULT_REGION` environment variable." + ) + + base_url = f"https://bedrock-mantle.{region}.api.aws/openai/v1" + + return _normalize_bedrock_base_url(base_url) + + +def _bedrock_token_provider(provider: BedrockTokenProvider) -> BedrockTokenProvider: + """Adapt a sync Bedrock token provider to the base client's api_key callback.""" + + def get_token() -> str: + token = cast(object, provider()) + if not isinstance(token, str) or not token: + raise ValueError(f"Expected `bedrock_token_provider` argument to return a string but it returned {token}") + + return token + + return get_token + + +def _async_bedrock_token_provider(provider: AsyncBedrockTokenProvider) -> Callable[[], Awaitable[str]]: + """Adapt a sync or async Bedrock token provider to the async api_key callback.""" + + async def get_token() -> str: + token = cast(object, provider()) + if inspect.isawaitable(token): + token = await token + + if not isinstance(token, str) or not token: + raise ValueError(f"Expected `bedrock_token_provider` argument to return a string but it returned {token}") + + return token + + return get_token + + +class BedrockOpenAI(OpenAI): + """API client for Amazon Bedrock's OpenAI-compatible endpoint.""" + + _bedrock_token_provider: BedrockTokenProvider | None + aws_region: str | None + + def __init__( + self, + *, + api_key: str | None = None, + bedrock_token_provider: BedrockTokenProvider | None = None, + aws_region: str | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: + """Construct a new synchronous Amazon Bedrock client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `AWS_BEARER_TOKEN_BEDROCK` + - `aws_region` from `AWS_REGION` or `AWS_DEFAULT_REGION` when `base_url` and `AWS_BEDROCK_BASE_URL` are not set + - `base_url` from `AWS_BEDROCK_BASE_URL` + + `bedrock_token_provider` is invoked before each request when provided. + """ + if api_key is None and bedrock_token_provider is None: + api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + + if callable(cast(object, api_key)): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + + if api_key is not None and bedrock_token_provider is not None: + raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") + + if _enforce_credentials and not api_key and bedrock_token_provider is None: + raise OpenAIError( + "Missing credentials. Please pass an `api_key` or `bedrock_token_provider`, or set the " + "`AWS_BEARER_TOKEN_BEDROCK` environment variable." + ) + + self._bedrock_token_provider = bedrock_token_provider + self.aws_region = aws_region + + super().__init__( + api_key=_bedrock_token_provider(bedrock_token_provider) + if bedrock_token_provider is not None + else api_key or "", + admin_api_key="", + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=_resolve_bedrock_base_url(base_url, aws_region), + websocket_base_url=websocket_base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _strict_response_validation=_strict_response_validation, + _enforce_credentials=False, + ) + + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if security.get("bearer_auth", False) or security.get("admin_api_key_auth", False): + return self._bearer_auth + + return {} + + @override + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if ( + self._api_key_provider is not None + and options.security.get("admin_api_key_auth", False) + and not options.security.get("bearer_auth", False) + ): + self._refresh_api_key() + + return super()._prepare_options(options) + + @override + def copy( + self, + *, + api_key: str | BedrockTokenProvider | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | None = None, + bedrock_token_provider: BedrockTokenProvider | None = None, + aws_region: str | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx.URL | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + if callable(api_key): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + + if admin_api_key is not None or workload_identity is not None: + raise OpenAIError("BedrockOpenAI only supports Bedrock bearer token authentication.") + + if api_key is not None and bedrock_token_provider is not None: + raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + next_token_provider = ( + bedrock_token_provider if bedrock_token_provider is not None else self._bedrock_token_provider + ) + next_api_key = api_key if api_key is not None else (None if next_token_provider is not None else self.api_key) + + return self.__class__( + api_key=next_api_key, + bedrock_token_provider=next_token_provider, + aws_region=aws_region if aws_region is not None else self.aws_region, + organization=organization if organization is not None else self.organization, + project=project if project is not None else self.project, + webhook_secret=webhook_secret if webhook_secret is not None else self.webhook_secret, + websocket_base_url=websocket_base_url if websocket_base_url is not None else self.websocket_base_url, + base_url=base_url if base_url is not None else self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client or self._client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, + **_extra_kwargs, + ) + + with_options = copy + + +class AsyncBedrockOpenAI(AsyncOpenAI): + """Async API client for Amazon Bedrock's OpenAI-compatible endpoint.""" + + _bedrock_token_provider: AsyncBedrockTokenProvider | None + aws_region: str | None + + def __init__( + self, + *, + api_key: str | None = None, + bedrock_token_provider: AsyncBedrockTokenProvider | None = None, + aws_region: str | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + _strict_response_validation: bool = False, + _enforce_credentials: bool = True, + ) -> None: + """Construct a new asynchronous Amazon Bedrock client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `AWS_BEARER_TOKEN_BEDROCK` + - `aws_region` from `AWS_REGION` or `AWS_DEFAULT_REGION` when `base_url` and `AWS_BEDROCK_BASE_URL` are not set + - `base_url` from `AWS_BEDROCK_BASE_URL` + + `bedrock_token_provider` is invoked before each request when provided. + """ + if api_key is None and bedrock_token_provider is None: + api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + + if callable(cast(object, api_key)): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + + if api_key is not None and bedrock_token_provider is not None: + raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") + + if _enforce_credentials and not api_key and bedrock_token_provider is None: + raise OpenAIError( + "Missing credentials. Please pass an `api_key` or `bedrock_token_provider`, or set the " + "`AWS_BEARER_TOKEN_BEDROCK` environment variable." + ) + + self._bedrock_token_provider = bedrock_token_provider + self.aws_region = aws_region + + super().__init__( + api_key=( + _async_bedrock_token_provider(bedrock_token_provider) + if bedrock_token_provider is not None + else api_key or "" + ), + admin_api_key="", + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=_resolve_bedrock_base_url(base_url, aws_region), + websocket_base_url=websocket_base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _strict_response_validation=_strict_response_validation, + _enforce_credentials=False, + ) + + @override + def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if security.get("bearer_auth", False) or security.get("admin_api_key_auth", False): + return self._bearer_auth + + return {} + + @override + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + if ( + self._api_key_provider is not None + and options.security.get("admin_api_key_auth", False) + and not options.security.get("bearer_auth", False) + ): + await self._refresh_api_key() + + return await super()._prepare_options(options) + + @override + def copy( + self, + *, + api_key: str | AsyncBedrockTokenProvider | None = None, + admin_api_key: str | None = None, + workload_identity: WorkloadIdentity | None = None, + bedrock_token_provider: AsyncBedrockTokenProvider | None = None, + aws_region: str | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx.URL | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _enforce_credentials: bool | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + if callable(api_key): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + + if admin_api_key is not None or workload_identity is not None: + raise OpenAIError("AsyncBedrockOpenAI only supports Bedrock bearer token authentication.") + + if api_key is not None and bedrock_token_provider is not None: + raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + next_token_provider = ( + bedrock_token_provider if bedrock_token_provider is not None else self._bedrock_token_provider + ) + next_api_key = api_key if api_key is not None else (None if next_token_provider is not None else self.api_key) + + return self.__class__( + api_key=next_api_key, + bedrock_token_provider=next_token_provider, + aws_region=aws_region if aws_region is not None else self.aws_region, + organization=organization if organization is not None else self.organization, + project=project if project is not None else self.project, + webhook_secret=webhook_secret if webhook_secret is not None else self.webhook_secret, + websocket_base_url=websocket_base_url if websocket_base_url is not None else self.websocket_base_url, + base_url=base_url if base_url is not None else self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client or self._client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, + **_extra_kwargs, + ) + + with_options = copy diff --git a/tests/lib/test_bedrock.py b/tests/lib/test_bedrock.py new file mode 100644 index 0000000000..9995e4c431 --- /dev/null +++ b/tests/lib/test_bedrock.py @@ -0,0 +1,439 @@ +from __future__ import annotations + +import json +from typing import Any, Union, Protocol, cast + +import httpx +import pytest +from httpx import URL +from respx import MockRouter + +from openai import OpenAIError, NotFoundError +from tests.utils import update_env +from openai._types import Omit +from openai.lib.bedrock import BedrockOpenAI, AsyncBedrockOpenAI + +Client = Union[BedrockOpenAI, AsyncBedrockOpenAI] + +RESPONSE_BODY: dict[str, Any] = { + "id": "resp_123", + "object": "response", + "created_at": 0, + "status": "completed", + "background": False, + "error": None, + "incomplete_details": None, + "instructions": None, + "max_output_tokens": None, + "max_tool_calls": None, + "model": "gpt-4o", + "output": [], + "parallel_tool_calls": True, + "previous_response_id": None, + "prompt_cache_key": None, + "reasoning": {"effort": None, "summary": None}, + "safety_identifier": None, + "service_tier": "default", + "store": True, + "temperature": 1.0, + "text": {"format": {"type": "text"}, "verbosity": "medium"}, + "tool_choice": "auto", + "tools": [], + "top_logprobs": 0, + "top_p": 1.0, + "truncation": "disabled", + "usage": { + "input_tokens": 0, + "input_tokens_details": {"cached_tokens": 0}, + "output_tokens": 0, + "output_tokens_details": {"reasoning_tokens": 0}, + "total_tokens": 0, + }, + "user": None, + "metadata": {}, +} +COMPACTED_RESPONSE_BODY: dict[str, Any] = { + "id": "resp_123", + "created_at": 0, + "object": "response.compaction", + "output": [], + "usage": RESPONSE_BODY["usage"], +} +INPUT_ITEMS_BODY: dict[str, Any] = { + "object": "list", + "data": [], + "first_id": "item_123", + "last_id": "item_123", + "has_more": False, +} +INPUT_TOKENS_BODY: dict[str, Any] = { + "object": "response.input_tokens", + "input_tokens": 1, +} + + +class MockRequestCall(Protocol): + request: httpx.Request + + +def make_sync_client(**kwargs: Any) -> BedrockOpenAI: + return BedrockOpenAI(http_client=httpx.Client(trust_env=False), **kwargs) + + +def make_async_client(**kwargs: Any) -> AsyncBedrockOpenAI: + return AsyncBedrockOpenAI(http_client=httpx.AsyncClient(trust_env=False), **kwargs) + + +def response_created_sse() -> str: + event: dict[str, Any] = {"type": "response.created", "sequence_number": 0, "response": RESPONSE_BODY} + return f"event: response.created\ndata: {json.dumps(event)}\n\n" + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_region_derived_base_url(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION="us-east-1", AWS_DEFAULT_REGION=Omit()): + client = ( + make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token") + ) + + assert client.base_url == URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_bedrock_config_precedence(client_cls: type[Client]) -> None: + with update_env( + AWS_BEDROCK_BASE_URL="https://env.example.com/openai/v1", + AWS_BEARER_TOKEN_BEDROCK="env token", + AWS_REGION="us-east-1", + AWS_DEFAULT_REGION="us-west-2", + ): + client = ( + make_sync_client( + base_url="https://explicit.example.com/openai/v1/responses", + api_key="explicit token", + ) + if client_cls is BedrockOpenAI + else make_async_client( + base_url="https://explicit.example.com/openai/v1/responses", + api_key="explicit token", + ) + ) + + assert client.base_url == URL("https://explicit.example.com/openai/v1/") + assert client.api_key == "explicit token" + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_bedrock_region_precedence(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION="us-east-1", AWS_DEFAULT_REGION="us-west-2"): + explicit_region_client = ( + make_sync_client(aws_region="eu-west-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(aws_region="eu-west-1", api_key="token") + ) + aws_region_client = ( + make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token") + ) + + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION="us-west-2"): + default_region_client = ( + make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token") + ) + + assert explicit_region_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/") + assert aws_region_client.base_url == URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/") + assert default_region_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_normalizes_responses_url(client_cls: type[Client]) -> None: + client = ( + make_sync_client(base_url="https://example.com/openai/v1/responses", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(base_url="https://example.com/openai/v1/responses", api_key="token") + ) + + assert client.base_url == URL("https://example.com/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_requires_endpoint_configuration(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): + with pytest.raises(OpenAIError, match="Must provide one of the `base_url` or `aws_region`"): + client_cls(api_key="token") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_does_not_use_openai_api_key(client_cls: type[Client]) -> None: + with update_env( + OPENAI_API_KEY="openai token", + AWS_BEARER_TOKEN_BEDROCK=Omit(), + AWS_BEDROCK_BASE_URL="https://example.com/openai/v1", + ): + with pytest.raises(OpenAIError, match="AWS_BEARER_TOKEN_BEDROCK"): + client_cls() + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_rejects_static_token_and_provider(client_cls: type[Client]) -> None: + with pytest.raises(OpenAIError, match="mutually exclusive"): + client_cls( + base_url="https://example.com/openai/v1", + api_key="token", + bedrock_token_provider=lambda: "provider token", + ) + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_requires_refreshable_tokens_to_use_provider_option(client_cls: type[Client]) -> None: + with pytest.raises(OpenAIError, match="bedrock_token_provider"): + client_cls( + base_url="https://example.com/openai/v1", + api_key=lambda: "provider token", # type: ignore[arg-type] + ) + + +@pytest.mark.respx() +def test_token_provider_refresh_sync(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/responses").mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response(200, json=RESPONSE_BODY), + ] + ) + tokens = iter(["first", "second"]) + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + bedrock_token_provider=lambda: next(tokens), + http_client=httpx.Client(trust_env=False), + max_retries=1, + ) + + client.responses.create(model="gpt-4o", input="hello") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers["Authorization"] == "Bearer first" + assert calls[1].request.headers["Authorization"] == "Bearer second" + + +@pytest.mark.asyncio +@pytest.mark.respx() +async def test_token_provider_refresh_async(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/responses").mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response(200, json=RESPONSE_BODY), + ] + ) + tokens = iter(["first", "second"]) + client = AsyncBedrockOpenAI( + base_url="https://example.com/openai/v1", + bedrock_token_provider=lambda: next(tokens), + http_client=httpx.AsyncClient(trust_env=False), + max_retries=1, + ) + + await client.responses.create(model="gpt-4o", input="hello") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers["Authorization"] == "Bearer first" + assert calls[1].request.headers["Authorization"] == "Bearer second" + + +def test_preserves_token_provider_across_with_options() -> None: + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + bedrock_token_provider=lambda: "provider token", + http_client=httpx.Client(trust_env=False), + ) + + copied_client = client.with_options(timeout=1) + + assert copied_client._refresh_api_key() == "provider token" + + +@pytest.mark.parametrize( + "copy_kwargs", + [ + {"admin_api_key": "admin token"}, + {"workload_identity": cast(Any, {})}, + ], +) +def test_rejects_non_bedrock_copy_auth(copy_kwargs: dict[str, Any]) -> None: + client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + + with pytest.raises(OpenAIError, match="only supports Bedrock bearer token authentication"): + client.with_options(**copy_kwargs) + + +@pytest.mark.respx() +def test_passes_non_responses_resources_through(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/chat/completions").mock( + return_value=httpx.Response( + 404, + json={"error": {"message": "AWS does not support chat completions here"}}, + headers={"x-request-id": "req_chat"}, + ) + ) + client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + + with pytest.raises(NotFoundError, match="AWS does not support chat completions here") as exc: + client.chat.completions.create(model="gpt-4o", messages=[]) + + assert exc.value.request_id == "req_chat" + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.url == URL("https://example.com/openai/v1/chat/completions") + + +@pytest.mark.asyncio +@pytest.mark.respx() +async def test_passes_non_responses_resources_through_async(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/chat/completions").mock( + return_value=httpx.Response( + 404, + json={"error": {"message": "AWS does not support chat completions here"}}, + headers={"x-request-id": "req_chat"}, + ) + ) + client = make_async_client(base_url="https://example.com/openai/v1", api_key="token") + + with pytest.raises(NotFoundError, match="AWS does not support chat completions here") as exc: + await client.chat.completions.create(model="gpt-4o", messages=[]) + + assert exc.value.request_id == "req_chat" + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.url == URL("https://example.com/openai/v1/chat/completions") + + +@pytest.mark.respx() +def test_passes_responses_features_through(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/responses").mock( + return_value=httpx.Response(200, json=RESPONSE_BODY) + ) + client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + + response = client.responses.create( + model="gpt-4o", + input="hello", + tools=[{"type": "web_search_preview"}], # type: ignore[list-item] + ) + + assert response.id == "resp_123" + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert json.loads(calls[0].request.content)["tools"] == [{"type": "web_search_preview"}] + + +@pytest.mark.respx() +def test_passes_admin_security_routes_through(respx_mock: MockRouter) -> None: + respx_mock.get("https://example.com/openai/v1/organization/invites").mock( + return_value=httpx.Response( + 404, + json={"error": {"message": "AWS does not support organization invites here"}}, + headers={"x-request-id": "req_admin"}, + ) + ) + client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + + with pytest.raises(NotFoundError, match="AWS does not support organization invites here"): + list(client.admin.organization.invites.list()) + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers["Authorization"] == "Bearer token" + + +@pytest.mark.respx() +def test_refreshes_token_provider_for_admin_security_routes(respx_mock: MockRouter) -> None: + respx_mock.get("https://example.com/openai/v1/organization/invites").mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response( + 404, + json={"error": {"message": "AWS does not support organization invites here"}}, + headers={"x-request-id": "req_admin"}, + ), + ] + ) + tokens = iter(["first", "second"]) + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + bedrock_token_provider=lambda: next(tokens), + http_client=httpx.Client(trust_env=False), + max_retries=1, + ) + + with pytest.raises(NotFoundError, match="AWS does not support organization invites here"): + list(client.admin.organization.invites.list()) + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert calls[0].request.headers["Authorization"] == "Bearer first" + assert calls[1].request.headers["Authorization"] == "Bearer second" + + +@pytest.mark.respx() +def test_allows_responses_http_methods(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/responses").mock( + return_value=httpx.Response(200, json=RESPONSE_BODY) + ) + respx_mock.get("https://example.com/openai/v1/responses/resp_123?starting_after=1&stream=true").mock( + return_value=httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"}) + ) + respx_mock.get("https://example.com/openai/v1/responses/resp_123?stream=true").mock( + return_value=httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"}) + ) + respx_mock.get("https://example.com/openai/v1/responses/resp_123").mock( + return_value=httpx.Response(200, json=RESPONSE_BODY) + ) + respx_mock.post("https://example.com/openai/v1/responses/resp_123/cancel").mock( + return_value=httpx.Response(200, json=RESPONSE_BODY) + ) + respx_mock.post("https://example.com/openai/v1/responses/compact").mock( + return_value=httpx.Response(200, json=COMPACTED_RESPONSE_BODY) + ) + respx_mock.get("https://example.com/openai/v1/responses/resp_123/input_items").mock( + return_value=httpx.Response(200, json=INPUT_ITEMS_BODY) + ) + respx_mock.post("https://example.com/openai/v1/responses/input_tokens").mock( + return_value=httpx.Response(200, json=INPUT_TOKENS_BODY) + ) + client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + + assert client.responses.create(model="gpt-4o", input="hello", background=True).id == "resp_123" + assert client.responses.retrieve("resp_123").id == "resp_123" + assert [event.type for event in client.responses.retrieve("resp_123", starting_after=1, stream=True)] == [ + "response.created" + ] + assert [event.type for event in client.responses.retrieve("resp_123", stream=True)] == ["response.created"] + assert client.responses.cancel("resp_123").id == "resp_123" + assert client.responses.compact(model="gpt-4o").object == "response.compaction" + assert list(client.responses.input_items.list("resp_123")) == [] + assert client.responses.input_tokens.count(model="gpt-4o", input="hello").input_tokens == 1 + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert {call.request.headers["Authorization"] for call in calls} == {"Bearer token"} + + +@pytest.mark.respx() +def test_allows_sse_and_response_wrappers(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/responses").mock( + side_effect=[ + httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"}), + httpx.Response(200, json=RESPONSE_BODY), + httpx.Response(200, json=RESPONSE_BODY), + ] + ) + client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + + events = list(client.responses.create(model="gpt-4o", input="hello", stream=True)) + assert [event.type for event in events] == ["response.created"] + + raw_response = client.responses.with_raw_response.create(model="gpt-4o", input="hello") + assert raw_response.parse().id == "resp_123" + + with client.responses.with_streaming_response.create(model="gpt-4o", input="hello") as response: + assert response.parse().id == "resp_123" + + +def test_does_not_guard_responses_connect() -> None: + client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + + assert client.responses.connect() is not None diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 6371ae7057..7889d4b41e 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -30,6 +30,8 @@ def reset_state() -> None: openai.azure_endpoint = None openai.azure_ad_token = None openai.azure_ad_token_provider = None + openai._bedrock_api_key = None + openai.bedrock_token_provider = None @pytest.fixture(autouse=True) @@ -102,6 +104,7 @@ def test_http_client_option() -> None: from typing import Iterator from openai.lib.azure import AzureOpenAI +from openai.lib.bedrock import BedrockOpenAI @contextlib.contextmanager @@ -184,3 +187,64 @@ def test_azure_azure_ad_token_provider_version_and_endpoint_env() -> None: assert isinstance(client, AzureOpenAI) assert client._azure_ad_token_provider is not None assert client._azure_ad_token_provider() == "token" + + +def test_bedrock_token_and_region_env() -> None: + with fresh_env(): + openai.api_type = "amazon-bedrock" + _os.environ["AWS_BEARER_TOKEN_BEDROCK"] = "example Bedrock token" + _os.environ["AWS_REGION"] = "us-west-2" + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + assert client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + + +def test_bedrock_api_type_env() -> None: + with fresh_env(): + _os.environ["OPENAI_API_TYPE"] = "amazon-bedrock" + _os.environ["AWS_BEARER_TOKEN_BEDROCK"] = "example Bedrock token" + _os.environ["AWS_REGION"] = "us-west-2" + reset_state() + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + assert openai.api_type == "amazon-bedrock" + + +def test_bedrock_api_type_uses_bedrock_credentials() -> None: + with fresh_env(): + openai.api_type = "amazon-bedrock" + _os.environ["OPENAI_API_KEY"] = "openai api key" + _os.environ["AWS_BEARER_TOKEN_BEDROCK"] = "example Bedrock token" + _os.environ["AWS_REGION"] = "us-west-2" + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + assert client.api_key == "example Bedrock token" + assert openai.api_key is None + + +def test_bedrock_api_type_uses_explicit_module_api_key() -> None: + with fresh_env(): + openai.api_type = "amazon-bedrock" + openai.api_key = "explicit Bedrock token" + _os.environ["AWS_BEARER_TOKEN_BEDROCK"] = "env Bedrock token" + _os.environ["AWS_REGION"] = "us-west-2" + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + assert client.api_key == "explicit Bedrock token" + assert openai.api_key == "explicit Bedrock token" + + +def test_bedrock_api_type_uses_token_provider_without_mutating_module_api_key() -> None: + with fresh_env(): + openai.api_type = "amazon-bedrock" + openai.bedrock_token_provider = lambda: "provider Bedrock token" + _os.environ["AWS_REGION"] = "us-west-2" + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + assert client._refresh_api_key() == "provider Bedrock token" + assert openai.api_key is None From 268de687ddfc8ab937372987ef71b99a57c05b0e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 18:41:18 +0000 Subject: [PATCH 374/408] feat(api): workload identity in audit logs, additional_tools item in responses, fix ActionSearch.query to be optional. --- .stats.yml | 4 +- src/openai/lib/_parsing/_responses.py | 1 + .../admin/organization/audit_logs.py | 12 ++ .../resources/chat/completions/completions.py | 48 ++++++++ .../resources/responses/input_tokens.py | 12 ++ src/openai/resources/responses/responses.py | 48 ++++++++ .../organization/audit_log_list_params.py | 6 + .../organization/audit_log_list_response.py | 114 ++++++++++++++++++ .../types/chat/completion_create_params.py | 8 ++ .../types/conversations/conversation_item.py | 17 +++ .../responses/input_token_count_params.py | 7 ++ src/openai/types/responses/parsed_response.py | 2 + src/openai/types/responses/response.py | 8 ++ .../types/responses/response_create_params.py | 8 ++ .../responses/response_function_web_search.py | 6 +- .../response_function_web_search_param.py | 6 +- .../types/responses/response_input_item.py | 17 +++ .../responses/response_input_item_param.py | 17 +++ .../types/responses/response_input_param.py | 17 +++ src/openai/types/responses/response_item.py | 17 +++ .../types/responses/response_output_item.py | 17 +++ .../types/responses/responses_client_event.py | 8 ++ .../responses/responses_client_event_param.py | 8 ++ .../responses/test_input_tokens.py | 2 + 24 files changed, 402 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index b9d21daa06..1accdac38a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 262 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-afacc4343d0efc074c8c5667eb83570642c8b9acaa7792ca8e075c6d18ef9f3a.yml -openapi_spec_hash: a62a557c61532681963fd21e748b0eb4 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-2a971ccbb946726988e96654eaecceb6d9b5ad27f5793c0064b864f5686d4fc1.yml +openapi_spec_hash: a712e4ee68f829570b3f5b267afa5a05 config_hash: bb69d8d0771dbac4a84fc6dca11e3ceb diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 8853a0749f..232718cef6 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -103,6 +103,7 @@ def parse_response( or output.type == "web_search_call" or output.type == "tool_search_call" or output.type == "tool_search_output" + or output.type == "additional_tools" or output.type == "reasoning" or output.type == "compaction" or output.type == "mcp_call" diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py index 3cf3127631..760fb4b034 100644 --- a/src/openai/resources/admin/organization/audit_logs.py +++ b/src/openai/resources/admin/organization/audit_logs.py @@ -91,6 +91,12 @@ def list( "tunnel.created", "tunnel.updated", "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", "role.created", "role.updated", "role.deleted", @@ -256,6 +262,12 @@ def list( "tunnel.created", "tunnel.updated", "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", "role.created", "role.updated", "role.deleted", diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index c85ac45cdb..9a2973c2a9 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -419,6 +419,14 @@ def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -735,6 +743,14 @@ def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1042,6 +1058,14 @@ def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -1943,6 +1967,14 @@ async def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -2259,6 +2291,14 @@ async def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently @@ -2566,6 +2606,14 @@ async def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning_effort: Constrains effort on reasoning for [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently diff --git a/src/openai/resources/responses/input_tokens.py b/src/openai/resources/responses/input_tokens.py index fae71fd59c..3306dfcd65 100644 --- a/src/openai/resources/responses/input_tokens.py +++ b/src/openai/resources/responses/input_tokens.py @@ -51,6 +51,7 @@ def count( instructions: Optional[str] | Omit = omit, model: Optional[str] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, text: Optional[input_token_count_params.Text] | Omit = omit, @@ -91,6 +92,10 @@ def count( parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). @@ -133,6 +138,7 @@ def count( "instructions": instructions, "model": model, "parallel_tool_calls": parallel_tool_calls, + "personality": personality, "previous_response_id": previous_response_id, "reasoning": reasoning, "text": text, @@ -181,6 +187,7 @@ async def count( instructions: Optional[str] | Omit = omit, model: Optional[str] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, text: Optional[input_token_count_params.Text] | Omit = omit, @@ -221,6 +228,10 @@ async def count( parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + previous_response_id: The unique ID of the previous response to the model. Use this to create multi-turn conversations. Learn more about [conversation state](https://platform.openai.com/docs/guides/conversation-state). @@ -263,6 +274,7 @@ async def count( "instructions": instructions, "model": model, "parallel_tool_calls": parallel_tool_calls, + "personality": personality, "previous_response_id": previous_response_id, "reasoning": reasoning, "text": text, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index e83f9824be..9caadacd66 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -268,6 +268,14 @@ def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning: **gpt-5 and o-series models only** @@ -525,6 +533,14 @@ def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning: **gpt-5 and o-series models only** @@ -775,6 +791,14 @@ def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning: **gpt-5 and o-series models only** @@ -1974,6 +1998,14 @@ async def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning: **gpt-5 and o-series models only** @@ -2231,6 +2263,14 @@ async def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning: **gpt-5 and o-series models only** @@ -2481,6 +2521,14 @@ async def create( prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. reasoning: **gpt-5 and o-series models only** diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py index bd3bc6d629..25224d47dc 100644 --- a/src/openai/types/admin/organization/audit_log_list_params.py +++ b/src/openai/types/admin/organization/audit_log_list_params.py @@ -81,6 +81,12 @@ class AuditLogListParams(TypedDict, total=False): "tunnel.created", "tunnel.updated", "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", "role.created", "role.updated", "role.deleted", diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py index ec899758a2..9fa99bd677 100644 --- a/src/openai/types/admin/organization/audit_log_list_response.py +++ b/src/openai/types/admin/organization/audit_log_list_response.py @@ -80,6 +80,12 @@ "UserDeleted", "UserUpdated", "UserUpdatedChangesRequested", + "WorkloadIdentityProviderMappingCreated", + "WorkloadIdentityProviderMappingDeleted", + "WorkloadIdentityProviderMappingUpdated", + "WorkloadIdentityProviderCreated", + "WorkloadIdentityProviderDeleted", + "WorkloadIdentityProviderUpdated", ] @@ -804,6 +810,78 @@ class UserUpdated(BaseModel): """The payload used to update the user.""" +class WorkloadIdentityProviderMappingCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider mapping ID.""" + + data: Optional[object] = None + """The payload used to create the workload identity provider mapping.""" + + identity_provider_id: Optional[str] = None + """The workload identity provider ID.""" + + +class WorkloadIdentityProviderMappingDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider mapping ID.""" + + identity_provider_id: Optional[str] = None + """The workload identity provider ID.""" + + project_id: Optional[str] = None + """The project ID.""" + + service_account_id: Optional[str] = None + """The mapped service account ID.""" + + +class WorkloadIdentityProviderMappingUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider mapping ID.""" + + changes_requested: Optional[object] = None + """The payload used to update the workload identity provider mapping.""" + + identity_provider_id: Optional[str] = None + """The workload identity provider ID.""" + + +class WorkloadIdentityProviderCreated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider ID.""" + + data: Optional[object] = None + """The payload used to create the workload identity provider.""" + + +class WorkloadIdentityProviderDeleted(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider ID.""" + + name: Optional[str] = None + """The workload identity provider name.""" + + +class WorkloadIdentityProviderUpdated(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The workload identity provider ID.""" + + changes_requested: Optional[object] = None + """The payload used to update the workload identity provider.""" + + class AuditLogListResponse(BaseModel): """A log of a user action or configuration change within this organization.""" @@ -852,6 +930,12 @@ class AuditLogListResponse(BaseModel): "tunnel.created", "tunnel.updated", "tunnel.deleted", + "workload_identity_provider.created", + "workload_identity_provider.updated", + "workload_identity_provider.deleted", + "workload_identity_provider_mapping.created", + "workload_identity_provider_mapping.updated", + "workload_identity_provider_mapping.deleted", "role.created", "role.updated", "role.deleted", @@ -1031,3 +1115,33 @@ class AuditLogListResponse(BaseModel): user_updated: Optional[UserUpdated] = FieldInfo(alias="user.updated", default=None) """The details for events with this `type`.""" + + workload_identity_provider_mapping_created: Optional[WorkloadIdentityProviderMappingCreated] = FieldInfo( + alias="workload_identity_provider_mapping.created", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_mapping_deleted: Optional[WorkloadIdentityProviderMappingDeleted] = FieldInfo( + alias="workload_identity_provider_mapping.deleted", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_mapping_updated: Optional[WorkloadIdentityProviderMappingUpdated] = FieldInfo( + alias="workload_identity_provider_mapping.updated", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_created: Optional[WorkloadIdentityProviderCreated] = FieldInfo( + alias="workload_identity_provider.created", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_deleted: Optional[WorkloadIdentityProviderDeleted] = FieldInfo( + alias="workload_identity_provider.deleted", default=None + ) + """The details for events with this `type`.""" + + workload_identity_provider_updated: Optional[WorkloadIdentityProviderUpdated] = FieldInfo( + alias="workload_identity_provider.updated", default=None + ) + """The details for events with this `type`.""" diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 3c541b96b4..5ec1a1ae91 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -191,6 +191,14 @@ class CompletionCreateParamsBase(TypedDict, total=False): Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. """ reasoning_effort: Optional[ReasoningEffort] diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py index 52e87ccb0b..9bc0495a34 100644 --- a/src/openai/types/conversations/conversation_item.py +++ b/src/openai/types/conversations/conversation_item.py @@ -6,6 +6,7 @@ from .message import Message from ..._utils import PropertyInfo from ..._models import BaseModel +from ..responses.tool import Tool from ..responses.response_reasoning_item import ResponseReasoningItem from ..responses.response_compaction_item import ResponseCompactionItem from ..responses.response_custom_tool_call import ResponseCustomToolCall @@ -27,6 +28,7 @@ __all__ = [ "ConversationItem", "ImageGenerationCall", + "AdditionalTools", "LocalShellCall", "LocalShellCallAction", "LocalShellCallOutput", @@ -54,6 +56,20 @@ class ImageGenerationCall(BaseModel): """The type of the image generation call. Always `image_generation_call`.""" +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[Tool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + class LocalShellCallAction(BaseModel): """Execute a shell command on the server.""" @@ -234,6 +250,7 @@ class McpCall(BaseModel): ResponseComputerToolCallOutputItem, ResponseToolSearchCall, ResponseToolSearchOutputItem, + AdditionalTools, ResponseReasoningItem, ResponseCompactionItem, ResponseCodeInterpreterToolCall, diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py index f8a2026537..d94eea689c 100644 --- a/src/openai/types/responses/input_token_count_params.py +++ b/src/openai/types/responses/input_token_count_params.py @@ -54,6 +54,13 @@ class InputTokenCountParams(TypedDict, total=False): parallel_tool_calls: Optional[bool] """Whether to allow the model to run tool calls in parallel.""" + personality: Union[str, Literal["friendly", "pragmatic"]] + """A model-owned style preset to apply to this request. + + Omit this parameter to use the model's default style. Supported values may + expand over time. Values must be at most 64 characters. + """ + previous_response_id: Optional[str] """The unique ID of the previous response to the model. diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index 4100a8d9d0..0d1f18b472 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -10,6 +10,7 @@ McpCall, McpListTools, LocalShellCall, + AdditionalTools, McpApprovalRequest, ImageGenerationCall, McpApprovalResponse, @@ -80,6 +81,7 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): ResponseComputerToolCallOutputItem, ResponseToolSearchCall, ResponseToolSearchOutputItem, + AdditionalTools, ResponseReasoningItem, McpCall, McpApprovalRequest, diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index dac3e09a89..bb4a4b3952 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -220,6 +220,14 @@ class Response(BaseModel): Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. """ reasoning: Optional[Reasoning] = None diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 5f9b948ae9..b85ee87741 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -158,6 +158,14 @@ class ResponseCreateParamsBase(TypedDict, total=False): Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. """ reasoning: Optional[Reasoning] diff --git a/src/openai/types/responses/response_function_web_search.py b/src/openai/types/responses/response_function_web_search.py index de6001e146..3584992b7d 100644 --- a/src/openai/types/responses/response_function_web_search.py +++ b/src/openai/types/responses/response_function_web_search.py @@ -29,15 +29,15 @@ class ActionSearchSource(BaseModel): class ActionSearch(BaseModel): """Action type "search" - Performs a web search query.""" - query: str - """[DEPRECATED] The search query.""" - type: Literal["search"] """The action type.""" queries: Optional[List[str]] = None """The search queries.""" + query: Optional[str] = None + """The search query.""" + sources: Optional[List[ActionSearchSource]] = None """The sources used in the search.""" diff --git a/src/openai/types/responses/response_function_web_search_param.py b/src/openai/types/responses/response_function_web_search_param.py index 15e313b0d3..9e31a46be1 100644 --- a/src/openai/types/responses/response_function_web_search_param.py +++ b/src/openai/types/responses/response_function_web_search_param.py @@ -30,15 +30,15 @@ class ActionSearchSource(TypedDict, total=False): class ActionSearch(TypedDict, total=False): """Action type "search" - Performs a web search query.""" - query: Required[str] - """[DEPRECATED] The search query.""" - type: Required[Literal["search"]] """The action type.""" queries: SequenceNotStr[str] """The search queries.""" + query: str + """The search query.""" + sources: Iterable[ActionSearchSource] """The sources used in the search.""" diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index 9f12b429bd..f7f67c3414 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -3,6 +3,7 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias +from .tool import Tool from ..._utils import PropertyInfo from ..._models import BaseModel from .local_environment import LocalEnvironment @@ -31,6 +32,7 @@ "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", "ToolSearchCall", + "AdditionalTools", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -170,6 +172,20 @@ class ToolSearchCall(BaseModel): """The status of the tool search call.""" +class AdditionalTools(BaseModel): + role: Literal["developer"] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: List[Tool] + """A list of additional tools made available at this item.""" + + type: Literal["additional_tools"] + """The item type. Always `additional_tools`.""" + + id: Optional[str] = None + """The unique ID of this additional tools item.""" + + class ImageGenerationCall(BaseModel): """An image generation request made by the model.""" @@ -558,6 +574,7 @@ class ItemReference(BaseModel): FunctionCallOutput, ToolSearchCall, ResponseToolSearchOutputItemParam, + AdditionalTools, ResponseReasoningItem, ResponseCompactionItemParam, ImageGenerationCall, diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 156ac92d39..0b0d68cdbd 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .tool_param import ToolParam from .local_environment_param import LocalEnvironmentParam from .easy_input_message_param import EasyInputMessageParam from .container_reference_param import ContainerReferenceParam @@ -32,6 +33,7 @@ "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", "ToolSearchCall", + "AdditionalTools", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -171,6 +173,20 @@ class ToolSearchCall(TypedDict, total=False): """The status of the tool search call.""" +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[ToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + class ImageGenerationCall(TypedDict, total=False): """An image generation request made by the model.""" @@ -555,6 +571,7 @@ class ItemReference(TypedDict, total=False): FunctionCallOutput, ToolSearchCall, ResponseToolSearchOutputItemParamParam, + AdditionalTools, ResponseReasoningItemParam, ResponseCompactionItemParamParam, ImageGenerationCall, diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index 656c70359b..f45aa276c8 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr +from .tool_param import ToolParam from .local_environment_param import LocalEnvironmentParam from .easy_input_message_param import EasyInputMessageParam from .container_reference_param import ContainerReferenceParam @@ -33,6 +34,7 @@ "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", "ToolSearchCall", + "AdditionalTools", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -172,6 +174,20 @@ class ToolSearchCall(TypedDict, total=False): """The status of the tool search call.""" +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[ToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + class ImageGenerationCall(TypedDict, total=False): """An image generation request made by the model.""" @@ -556,6 +572,7 @@ class ItemReference(TypedDict, total=False): FunctionCallOutput, ToolSearchCall, ResponseToolSearchOutputItemParamParam, + AdditionalTools, ResponseReasoningItemParam, ResponseCompactionItemParamParam, ImageGenerationCall, diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py index 721bf02ecb..eb2cd44192 100644 --- a/src/openai/types/responses/response_item.py +++ b/src/openai/types/responses/response_item.py @@ -3,6 +3,7 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias +from .tool import Tool from ..._utils import PropertyInfo from ..._models import BaseModel from .response_output_message import ResponseOutputMessage @@ -27,6 +28,7 @@ __all__ = [ "ResponseItem", + "AdditionalTools", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -39,6 +41,20 @@ ] +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[Tool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + class ImageGenerationCall(BaseModel): """An image generation request made by the model.""" @@ -235,6 +251,7 @@ class McpCall(BaseModel): ResponseFunctionToolCallOutputItem, ResponseToolSearchCall, ResponseToolSearchOutputItem, + AdditionalTools, ResponseReasoningItem, ResponseCompactionItem, ImageGenerationCall, diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index a4b23f26fd..ee7057348b 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -3,6 +3,7 @@ from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias +from .tool import Tool from ..._utils import PropertyInfo from ..._models import BaseModel from .response_output_message import ResponseOutputMessage @@ -26,6 +27,7 @@ __all__ = [ "ResponseOutputItem", + "AdditionalTools", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -38,6 +40,20 @@ ] +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[Tool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + class ImageGenerationCall(BaseModel): """An image generation request made by the model.""" @@ -234,6 +250,7 @@ class McpApprovalResponse(BaseModel): ResponseReasoningItem, ResponseToolSearchCall, ResponseToolSearchOutputItem, + AdditionalTools, ResponseCompactionItem, ImageGenerationCall, ResponseCodeInterpreterToolCall, diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py index 0a5dcb4ddd..8f50848d9d 100644 --- a/src/openai/types/responses/responses_client_event.py +++ b/src/openai/types/responses/responses_client_event.py @@ -190,6 +190,14 @@ class ResponsesClientEvent(BaseModel): Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. """ reasoning: Optional[Reasoning] = None diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py index 59d8f205ae..632807aedf 100644 --- a/src/openai/types/responses/responses_client_event_param.py +++ b/src/openai/types/responses/responses_client_event_param.py @@ -191,6 +191,14 @@ class ResponsesClientEventParam(TypedDict, total=False): Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. """ reasoning: Optional[Reasoning] diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py index b4bc627837..04c0bb698e 100644 --- a/tests/api_resources/responses/test_input_tokens.py +++ b/tests/api_resources/responses/test_input_tokens.py @@ -30,6 +30,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None: instructions="instructions", model="model", parallel_tool_calls=True, + personality="friendly", previous_response_id="resp_123", reasoning={ "effort": "none", @@ -94,6 +95,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> instructions="instructions", model="model", parallel_tool_calls=True, + personality="friendly", previous_response_id="resp_123", reasoning={ "effort": "none", From e4bccc79d09fec54ad5ff8ec5c55ebdda2ecbef9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 18:43:01 +0000 Subject: [PATCH 375/408] release: 2.39.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 0e1c697558..9f6026f1c7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.38.0" + ".": "2.39.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b4f9d758d8..13992c0e52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.39.0 (2026-06-01) + +Full Changelog: [v2.38.0...v2.39.0](https://github.com/openai/openai-python/compare/v2.38.0...v2.39.0) + +### Features + +* **api:** workload identity in audit logs, additional_tools item in responses, fix ActionSearch.query to be optional. ([ab60d7a](https://github.com/openai/openai-python/commit/ab60d7a52c310bb0490ff36b8bdc33b8d4ea725f)) + ## 2.38.0 (2026-05-21) Full Changelog: [v2.37.0...v2.38.0](https://github.com/openai/openai-python/compare/v2.37.0...v2.38.0) diff --git a/pyproject.toml b/pyproject.toml index aedb6d8f51..39c6b5174d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.38.0" +version = "2.39.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index ba9c5f3a3c..8f492efa5d 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.38.0" # x-release-please-version +__version__ = "2.39.0" # x-release-please-version From fdf4901e301fa01b368ede0b5b407dca42f07acc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 19:09:39 +0000 Subject: [PATCH 376/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1accdac38a..7345dd30b5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 262 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-2a971ccbb946726988e96654eaecceb6d9b5ad27f5793c0064b864f5686d4fc1.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-a6fedde02dc6241719ebb2589062ba2892baa8dbe928a2d718643beede85e7b3.yml openapi_spec_hash: a712e4ee68f829570b3f5b267afa5a05 -config_hash: bb69d8d0771dbac4a84fc6dca11e3ceb +config_hash: e02ca1082421dfe55b145c45e95d6126 From a50ff0a19084306a09012ff85f730ea2c129eef9 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Mon, 1 Jun 2026 17:25:30 -0400 Subject: [PATCH 377/408] Fix Bedrock with_options overrides --- src/openai/lib/bedrock.py | 45 ++++++++++++++++++++++++++++------- tests/lib/test_bedrock.py | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/openai/lib/bedrock.py b/src/openai/lib/bedrock.py index 0a1aec36ee..266a2e9358 100644 --- a/src/openai/lib/bedrock.py +++ b/src/openai/lib/bedrock.py @@ -54,6 +54,17 @@ def _resolve_bedrock_base_url(base_url: str | httpx.URL | None, aws_region: str return _normalize_bedrock_base_url(base_url) +def _uses_region_derived_bedrock_base_url(base_url: str | httpx.URL | None) -> bool: + if isinstance(base_url, str) and not base_url.strip(): + base_url = None + + if base_url is not None: + return False + + env_base_url = os.environ.get("AWS_BEDROCK_BASE_URL") + return env_base_url is None or not env_base_url.strip() + + def _bedrock_token_provider(provider: BedrockTokenProvider) -> BedrockTokenProvider: """Adapt a sync Bedrock token provider to the base client's api_key callback.""" @@ -87,6 +98,7 @@ class BedrockOpenAI(OpenAI): """API client for Amazon Bedrock's OpenAI-compatible endpoint.""" _bedrock_token_provider: BedrockTokenProvider | None + _uses_region_derived_base_url: bool aws_region: str | None def __init__( @@ -133,6 +145,7 @@ def __init__( ) self._bedrock_token_provider = bedrock_token_provider + self._uses_region_derived_base_url = _uses_region_derived_bedrock_base_url(base_url) self.aws_region = aws_region super().__init__( @@ -223,10 +236,17 @@ def copy( elif set_default_query is not None: params = set_default_query - next_token_provider = ( - bedrock_token_provider if bedrock_token_provider is not None else self._bedrock_token_provider - ) + if api_key is not None: + next_token_provider = None + elif bedrock_token_provider is not None: + next_token_provider = bedrock_token_provider + else: + next_token_provider = self._bedrock_token_provider + next_api_key = api_key if api_key is not None else (None if next_token_provider is not None else self.api_key) + next_base_url = base_url + if next_base_url is None and not (aws_region is not None and self._uses_region_derived_base_url): + next_base_url = self.base_url return self.__class__( api_key=next_api_key, @@ -236,7 +256,7 @@ def copy( project=project if project is not None else self.project, webhook_secret=webhook_secret if webhook_secret is not None else self.webhook_secret, websocket_base_url=websocket_base_url if websocket_base_url is not None else self.websocket_base_url, - base_url=base_url if base_url is not None else self.base_url, + base_url=next_base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client or self._client, max_retries=max_retries if is_given(max_retries) else self.max_retries, @@ -253,6 +273,7 @@ class AsyncBedrockOpenAI(AsyncOpenAI): """Async API client for Amazon Bedrock's OpenAI-compatible endpoint.""" _bedrock_token_provider: AsyncBedrockTokenProvider | None + _uses_region_derived_base_url: bool aws_region: str | None def __init__( @@ -299,6 +320,7 @@ def __init__( ) self._bedrock_token_provider = bedrock_token_provider + self._uses_region_derived_base_url = _uses_region_derived_bedrock_base_url(base_url) self.aws_region = aws_region super().__init__( @@ -391,10 +413,17 @@ def copy( elif set_default_query is not None: params = set_default_query - next_token_provider = ( - bedrock_token_provider if bedrock_token_provider is not None else self._bedrock_token_provider - ) + if api_key is not None: + next_token_provider = None + elif bedrock_token_provider is not None: + next_token_provider = bedrock_token_provider + else: + next_token_provider = self._bedrock_token_provider + next_api_key = api_key if api_key is not None else (None if next_token_provider is not None else self.api_key) + next_base_url = base_url + if next_base_url is None and not (aws_region is not None and self._uses_region_derived_base_url): + next_base_url = self.base_url return self.__class__( api_key=next_api_key, @@ -404,7 +433,7 @@ def copy( project=project if project is not None else self.project, webhook_secret=webhook_secret if webhook_secret is not None else self.webhook_secret, websocket_base_url=websocket_base_url if websocket_base_url is not None else self.websocket_base_url, - base_url=base_url if base_url is not None else self.base_url, + base_url=next_base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client or self._client, max_retries=max_retries if is_given(max_retries) else self.max_retries, diff --git a/tests/lib/test_bedrock.py b/tests/lib/test_bedrock.py index 9995e4c431..dab9abd1cf 100644 --- a/tests/lib/test_bedrock.py +++ b/tests/lib/test_bedrock.py @@ -252,6 +252,55 @@ def test_preserves_token_provider_across_with_options() -> None: assert copied_client._refresh_api_key() == "provider token" +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_with_options_api_key_replaces_token_provider(client_cls: type[Client]) -> None: + client = ( + make_sync_client( + base_url="https://example.com/openai/v1", + bedrock_token_provider=lambda: "provider token", + ) + if client_cls is BedrockOpenAI + else make_async_client( + base_url="https://example.com/openai/v1", + bedrock_token_provider=lambda: "provider token", + ) + ) + + copied_client = client.with_options(api_key="static token") + + assert copied_client.api_key == "static token" + assert copied_client._bedrock_token_provider is None + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_with_options_aws_region_recomputes_region_derived_base_url(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): + client = ( + make_sync_client(aws_region="us-east-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(aws_region="us-east-1", api_key="token") + ) + + copied_client = client.with_options(aws_region="eu-west-1") + + assert copied_client.aws_region == "eu-west-1" + assert copied_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_with_options_aws_region_keeps_explicit_base_url(client_cls: type[Client]) -> None: + client = ( + make_sync_client(base_url="https://example.com/openai/v1", aws_region="us-east-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(base_url="https://example.com/openai/v1", aws_region="us-east-1", api_key="token") + ) + + copied_client = client.with_options(aws_region="eu-west-1") + + assert copied_client.aws_region == "eu-west-1" + assert copied_client.base_url == URL("https://example.com/openai/v1/") + + @pytest.mark.parametrize( "copy_kwargs", [ From 4d5bfdec37fa8a2b2a0413724755e586e627e28d Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Mon, 1 Jun 2026 17:39:17 -0400 Subject: [PATCH 378/408] fix(api): allow setting bedrock api keys on the client directly --- src/openai/__init__.py | 2 +- tests/test_module_client.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 8d4265970e..3786d106cb 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -305,7 +305,7 @@ class _BedrockModuleClient(_ModuleClient, BedrockOpenAI): # type: ignore @property # type: ignore @override def api_key(self) -> str | None: - return _bedrock_api_key if _bedrock_api_key is not None else api_key + return api_key if api_key is not None else _bedrock_api_key @api_key.setter # type: ignore def api_key(self, value: str | None) -> None: # type: ignore diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 7889d4b41e..23bcb61716 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -238,6 +238,21 @@ def test_bedrock_api_type_uses_explicit_module_api_key() -> None: assert openai.api_key == "explicit Bedrock token" +def test_bedrock_module_api_key_overrides_cached_env_token_after_load() -> None: + with fresh_env(): + openai.api_type = "amazon-bedrock" + _os.environ["AWS_BEARER_TOKEN_BEDROCK"] = "env Bedrock token" + _os.environ["AWS_REGION"] = "us-west-2" + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + assert client.api_key == "env Bedrock token" + + openai.api_key = "new Bedrock token" + + assert client.api_key == "new Bedrock token" + + def test_bedrock_api_type_uses_token_provider_without_mutating_module_api_key() -> None: with fresh_env(): openai.api_type = "amazon-bedrock" From 2264f700dad91e4f570eb7c0a6f10bbd22d34520 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 21:40:16 +0000 Subject: [PATCH 379/408] release: 2.40.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9f6026f1c7..69efe8f538 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.39.0" + ".": "2.40.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 13992c0e52..0517344e33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.40.0 (2026-06-01) + +Full Changelog: [v2.39.0...v2.40.0](https://github.com/openai/openai-python/compare/v2.39.0...v2.40.0) + +### Bug Fixes + +* **api:** allow setting bedrock api keys on the client directly ([4d5bfde](https://github.com/openai/openai-python/commit/4d5bfdec37fa8a2b2a0413724755e586e627e28d)) + ## 2.39.0 (2026-06-01) Full Changelog: [v2.38.0...v2.39.0](https://github.com/openai/openai-python/compare/v2.38.0...v2.39.0) diff --git a/pyproject.toml b/pyproject.toml index 39c6b5174d..37cd1a8db1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.39.0" +version = "2.40.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 8f492efa5d..7db77fcfff 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.39.0" # x-release-please-version +__version__ = "2.40.0" # x-release-please-version From db6ccafa7b74b72caefbda6fb63bd5c904521770 Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Mon, 1 Jun 2026 17:41:39 -0400 Subject: [PATCH 380/408] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0517344e33..5d1dc4baba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Full Changelog: [v2.39.0...v2.40.0](https://github.com/openai/openai-python/compare/v2.39.0...v2.40.0) +### Features +* **api:** Add Amazon Bedrock Responses support + ### Bug Fixes * **api:** allow setting bedrock api keys on the client directly ([4d5bfde](https://github.com/openai/openai-python/commit/4d5bfdec37fa8a2b2a0413724755e586e627e28d)) From 87e46c25ac9ca8cff407b52ad9fb33e326c059d6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:32:39 +0000 Subject: [PATCH 381/408] feat(api): responses.moderation and chat_completions.moderation --- .stats.yml | 4 +- src/openai/auth/_workload.py | 1 + .../resources/chat/completions/completions.py | 30 ++++ src/openai/resources/responses/responses.py | 38 +++++ src/openai/types/chat/chat_completion.py | 160 +++++++++++++++++- .../types/chat/chat_completion_chunk.py | 152 ++++++++++++++++- .../types/chat/completion_create_params.py | 14 ++ src/openai/types/responses/response.py | 129 +++++++++++++- .../types/responses/response_create_params.py | 14 ++ .../types/responses/responses_client_event.py | 15 +- .../responses/responses_client_event_param.py | 22 ++- tests/api_resources/chat/test_completions.py | 4 + tests/api_resources/test_responses.py | 4 + tests/lib/chat/test_completions.py | 6 + tests/lib/chat/test_completions_streaming.py | 1 + 15 files changed, 581 insertions(+), 13 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7345dd30b5..a62e7b979c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 262 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-a6fedde02dc6241719ebb2589062ba2892baa8dbe928a2d718643beede85e7b3.yml -openapi_spec_hash: a712e4ee68f829570b3f5b267afa5a05 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0161cb2a0faadedfc17ff59c7fcad9671962dbd170f83811003c23702148cf36.yml +openapi_spec_hash: 1023c7442d0e2e7e213e8b3a253921c5 config_hash: e02ca1082421dfe55b145c45e95d6126 diff --git a/src/openai/auth/_workload.py b/src/openai/auth/_workload.py index 6c142a82ed..6b13ededb2 100644 --- a/src/openai/auth/_workload.py +++ b/src/openai/auth/_workload.py @@ -28,6 +28,7 @@ class SubjectTokenProvider(TypedDict): class WorkloadIdentity(TypedDict): """Identity provider resource id in WIFAPI.""" + identity_provider_id: str """Service account id to bind the verified external identity to.""" diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 9a2973c2a9..835adfde1c 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -104,6 +104,7 @@ def parse( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -204,6 +205,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "max_tokens": max_tokens, "metadata": metadata, "modalities": modalities, + "moderation": moderation, "n": n, "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, @@ -260,6 +262,7 @@ def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -396,6 +399,8 @@ def create( `["text", "audio"]` + moderation: Configuration for running moderation on the request input and generated output. + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -576,6 +581,7 @@ def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -720,6 +726,8 @@ def create( `["text", "audio"]` + moderation: Configuration for running moderation on the request input and generated output. + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -891,6 +899,7 @@ def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -1035,6 +1044,8 @@ def create( `["text", "audio"]` + moderation: Configuration for running moderation on the request input and generated output. + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -1205,6 +1216,7 @@ def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -1252,6 +1264,7 @@ def create( "max_tokens": max_tokens, "metadata": metadata, "modalities": modalities, + "moderation": moderation, "n": n, "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, @@ -1501,6 +1514,7 @@ def stream( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -1572,6 +1586,7 @@ def stream( max_tokens=max_tokens, metadata=metadata, modalities=modalities, + moderation=moderation, n=n, parallel_tool_calls=parallel_tool_calls, prediction=prediction, @@ -1652,6 +1667,7 @@ async def parse( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -1752,6 +1768,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "max_tokens": max_tokens, "metadata": metadata, "modalities": modalities, + "moderation": moderation, "n": n, "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, @@ -1808,6 +1825,7 @@ async def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -1944,6 +1962,8 @@ async def create( `["text", "audio"]` + moderation: Configuration for running moderation on the request input and generated output. + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -2124,6 +2144,7 @@ async def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -2268,6 +2289,8 @@ async def create( `["text", "audio"]` + moderation: Configuration for running moderation on the request input and generated output. + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -2439,6 +2462,7 @@ async def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -2583,6 +2607,8 @@ async def create( `["text", "audio"]` + moderation: Configuration for running moderation on the request input and generated output. + n: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. @@ -2753,6 +2779,7 @@ async def create( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -2800,6 +2827,7 @@ async def create( "max_tokens": max_tokens, "metadata": metadata, "modalities": modalities, + "moderation": moderation, "n": n, "parallel_tool_calls": parallel_tool_calls, "prediction": prediction, @@ -3049,6 +3077,7 @@ def stream( max_tokens: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, modalities: Optional[List[Literal["text", "audio"]]] | Omit = omit, + moderation: Optional[completion_create_params.Moderation] | Omit = omit, n: Optional[int] | Omit = omit, parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, @@ -3121,6 +3150,7 @@ def stream( max_tokens=max_tokens, metadata=metadata, modalities=modalities, + moderation=moderation, n=n, parallel_tool_calls=parallel_tool_calls, prediction=prediction, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 9caadacd66..5019d7e831 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -142,6 +142,7 @@ def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -250,6 +251,8 @@ def create( [model guide](https://platform.openai.com/docs/models) to browse and compare available models. + moderation: Configuration for running moderation on the input and output of this response. + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. previous_response_id: The unique ID of the previous response to the model. Use this to create @@ -401,6 +404,7 @@ def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -515,6 +519,8 @@ def create( [model guide](https://platform.openai.com/docs/models) to browse and compare available models. + moderation: Configuration for running moderation on the input and output of this response. + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. previous_response_id: The unique ID of the previous response to the model. Use this to create @@ -659,6 +665,7 @@ def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -773,6 +780,8 @@ def create( [model guide](https://platform.openai.com/docs/models) to browse and compare available models. + moderation: Configuration for running moderation on the input and output of this response. + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. previous_response_id: The unique ID of the previous response to the model. Use this to create @@ -915,6 +924,7 @@ def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -955,6 +965,7 @@ def create( "max_tool_calls": max_tool_calls, "metadata": metadata, "model": model, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, @@ -1023,6 +1034,7 @@ def stream( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, @@ -1064,6 +1076,7 @@ def stream( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, @@ -1098,6 +1111,7 @@ def stream( "max_output_tokens": max_output_tokens, "max_tool_calls": max_tool_calls, "metadata": metadata, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, @@ -1154,6 +1168,7 @@ def stream( max_output_tokens=max_output_tokens, max_tool_calls=max_tool_calls, metadata=metadata, + moderation=moderation, parallel_tool_calls=parallel_tool_calls, previous_response_id=previous_response_id, prompt=prompt, @@ -1214,6 +1229,7 @@ def parse( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -1273,6 +1289,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "max_tool_calls": max_tool_calls, "metadata": metadata, "model": model, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, @@ -1872,6 +1889,7 @@ async def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -1980,6 +1998,8 @@ async def create( [model guide](https://platform.openai.com/docs/models) to browse and compare available models. + moderation: Configuration for running moderation on the input and output of this response. + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. previous_response_id: The unique ID of the previous response to the model. Use this to create @@ -2131,6 +2151,7 @@ async def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -2245,6 +2266,8 @@ async def create( [model guide](https://platform.openai.com/docs/models) to browse and compare available models. + moderation: Configuration for running moderation on the input and output of this response. + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. previous_response_id: The unique ID of the previous response to the model. Use this to create @@ -2389,6 +2412,7 @@ async def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -2503,6 +2527,8 @@ async def create( [model guide](https://platform.openai.com/docs/models) to browse and compare available models. + moderation: Configuration for running moderation on the input and output of this response. + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. previous_response_id: The unique ID of the previous response to the model. Use this to create @@ -2645,6 +2671,7 @@ async def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -2685,6 +2712,7 @@ async def create( "max_tool_calls": max_tool_calls, "metadata": metadata, "model": model, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, @@ -2753,6 +2781,7 @@ def stream( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, @@ -2794,6 +2823,7 @@ def stream( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, @@ -2828,6 +2858,7 @@ def stream( "max_output_tokens": max_output_tokens, "max_tool_calls": max_tool_calls, "metadata": metadata, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, @@ -2884,6 +2915,7 @@ def stream( max_output_tokens=max_output_tokens, max_tool_calls=max_tool_calls, metadata=metadata, + moderation=moderation, parallel_tool_calls=parallel_tool_calls, previous_response_id=previous_response_id, prompt=prompt, @@ -2948,6 +2980,7 @@ async def parse( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -3007,6 +3040,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "max_tool_calls": max_tool_calls, "metadata": metadata, "model": model, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, @@ -4645,6 +4679,7 @@ def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[responses_client_event_param.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -4681,6 +4716,7 @@ def create( "max_tool_calls": max_tool_calls, "metadata": metadata, "model": model, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, @@ -4725,6 +4761,7 @@ async def create( max_tool_calls: Optional[int] | Omit = omit, metadata: Optional[Metadata] | Omit = omit, model: ResponsesModel | Omit = omit, + moderation: Optional[responses_client_event_param.Moderation] | Omit = omit, parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, @@ -4761,6 +4798,7 @@ async def create( "max_tool_calls": max_tool_calls, "metadata": metadata, "model": model, + "moderation": moderation, "parallel_tool_calls": parallel_tool_calls, "previous_response_id": previous_response_id, "prompt": prompt, diff --git a/src/openai/types/chat/chat_completion.py b/src/openai/types/chat/chat_completion.py index 31219aa812..2c4a78cd35 100644 --- a/src/openai/types/chat/chat_completion.py +++ b/src/openai/types/chat/chat_completion.py @@ -1,14 +1,28 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional -from typing_extensions import Literal +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel from ..completion_usage import CompletionUsage from .chat_completion_message import ChatCompletionMessage from .chat_completion_token_logprob import ChatCompletionTokenLogprob -__all__ = ["ChatCompletion", "Choice", "ChoiceLogprobs"] +__all__ = [ + "ChatCompletion", + "Choice", + "ChoiceLogprobs", + "Moderation", + "ModerationInput", + "ModerationInputModerationResults", + "ModerationInputModerationResultsResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResults", + "ModerationOutputModerationResultsResult", + "ModerationOutputError", +] class ChoiceLogprobs(BaseModel): @@ -29,7 +43,8 @@ class Choice(BaseModel): sequence, `length` if the maximum number of tokens specified in the request was reached, `content_filter` if content was omitted due to a flag from our content filters, `tool_calls` if the model called a tool, or `function_call` - (deprecated) if the model called a function. + (deprecated) if the model called a function. Read the + [Model Spec](https://model-spec.openai.com/2025-12-18.html) for more. """ index: int @@ -42,6 +57,137 @@ class Choice(BaseModel): """A chat completion message generated by the model.""" +class ModerationInputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationInputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResults, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationOutputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResults, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """ + Moderation results for the request input and generated output, if moderated + completions were requested. + """ + + input: ModerationInput + """Moderation for the request input.""" + + output: ModerationOutput + """Moderation for the generated output.""" + + class ChatCompletion(BaseModel): """ Represents a chat completion response returned by model, based on the provided input. @@ -65,6 +211,12 @@ class ChatCompletion(BaseModel): object: Literal["chat.completion"] """The object type, which is always `chat.completion`.""" + moderation: Optional[Moderation] = None + """ + Moderation results for the request input and generated output, if moderated + completions were requested. + """ + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] = None """Specifies the processing type used for serving the request. diff --git a/src/openai/types/chat/chat_completion_chunk.py b/src/openai/types/chat/chat_completion_chunk.py index ecbfd0a5aa..d983336fab 100644 --- a/src/openai/types/chat/chat_completion_chunk.py +++ b/src/openai/types/chat/chat_completion_chunk.py @@ -1,8 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional -from typing_extensions import Literal +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel from ..completion_usage import CompletionUsage from .chat_completion_token_logprob import ChatCompletionTokenLogprob @@ -15,6 +16,15 @@ "ChoiceDeltaToolCall", "ChoiceDeltaToolCallFunction", "ChoiceLogprobs", + "Moderation", + "ModerationInput", + "ModerationInputModerationResults", + "ModerationInputModerationResultsResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResults", + "ModerationOutputModerationResultsResult", + "ModerationOutputError", ] @@ -114,6 +124,138 @@ class Choice(BaseModel): """Log probability information for the choice.""" +class ModerationInputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationInputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResults, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResultsResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputModerationResults(BaseModel): + """Successful moderation results for the request input or generated output.""" + + model: str + """The moderation model used to generate the results.""" + + results: List[ModerationOutputModerationResultsResult] + """A list of moderation results.""" + + type: Literal["moderation_results"] + """The object type, which is always `moderation_results`.""" + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which is always `error`.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResults, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """Moderation results for the request input and generated output. + + Present + on the moderation chunk when moderated completions are requested. + """ + + input: ModerationInput + """Moderation for the request input.""" + + output: ModerationOutput + """Moderation for the generated output.""" + + class ChatCompletionChunk(BaseModel): """ Represents a streamed chunk of a chat completion response returned @@ -143,6 +285,12 @@ class ChatCompletionChunk(BaseModel): object: Literal["chat.completion.chunk"] """The object type, which is always `chat.completion.chunk`.""" + moderation: Optional[Moderation] = None + """Moderation results for the request input and generated output. + + Present on the moderation chunk when moderated completions are requested. + """ + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] = None """Specifies the processing type used for serving the request. diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index 5ec1a1ae91..b7de79be72 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -25,6 +25,7 @@ "CompletionCreateParamsBase", "FunctionCall", "Function", + "Moderation", "ResponseFormat", "WebSearchOptions", "WebSearchOptionsUserLocation", @@ -151,6 +152,9 @@ class CompletionCreateParamsBase(TypedDict, total=False): `["text", "audio"]` """ + moderation: Optional[Moderation] + """Configuration for running moderation on the request input and generated output.""" + n: Optional[int] """How many chat completion choices to generate for each input message. @@ -388,6 +392,16 @@ class Function(TypedDict, total=False): """ +class Moderation(TypedDict, total=False): + """Configuration for running moderation on the request input and generated output.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index bb4a4b3952..67102d2628 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional -from typing_extensions import Literal, TypeAlias +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias from .tool import Tool +from ..._utils import PropertyInfo from ..._models import BaseModel from .response_error import ResponseError from .response_usage import ResponseUsage @@ -24,7 +25,19 @@ from ..shared.responses_model import ResponsesModel from .tool_choice_apply_patch import ToolChoiceApplyPatch -__all__ = ["Response", "IncompleteDetails", "ToolChoice", "Conversation"] +__all__ = [ + "Response", + "IncompleteDetails", + "ToolChoice", + "Conversation", + "Moderation", + "ModerationInput", + "ModerationInputModerationResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResult", + "ModerationOutputError", +] class IncompleteDetails(BaseModel): @@ -56,6 +69,110 @@ class Conversation(BaseModel): """The unique ID of the conversation that this response was associated with.""" +class ModerationInputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResult, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResult, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """ + Moderation results for the response input and output, if moderated completions were requested. + """ + + input: ModerationInput + """Moderation for the response input.""" + + output: ModerationOutput + """Moderation for the response output.""" + + class Response(BaseModel): id: str """Unique identifier for this Response.""" @@ -193,6 +310,12 @@ class Response(BaseModel): ignored. """ + moderation: Optional[Moderation] = None + """ + Moderation results for the response input and output, if moderated completions + were requested. + """ + previous_response_id: Optional[str] = None """The unique ID of the previous response to the model. diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index b85ee87741..1b273928a1 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -27,6 +27,7 @@ "ResponseCreateParamsBase", "ContextManagement", "Conversation", + "Moderation", "StreamOptions", "ToolChoice", "ResponseCreateParamsNonStreaming", @@ -128,6 +129,9 @@ class ResponseCreateParamsBase(TypedDict, total=False): available models. """ + moderation: Optional[Moderation] + """Configuration for running moderation on the input and output of this response.""" + parallel_tool_calls: Optional[bool] """Whether to allow the model to run tool calls in parallel.""" @@ -304,6 +308,16 @@ class ContextManagement(TypedDict, total=False): Conversation: TypeAlias = Union[str, ResponseConversationParamParam] +class Moderation(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + class StreamOptions(TypedDict, total=False): """Options for streaming responses. Only set this when you set `stream: true`.""" diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py index 8f50848d9d..df33c019f1 100644 --- a/src/openai/types/responses/responses_client_event.py +++ b/src/openai/types/responses/responses_client_event.py @@ -22,7 +22,7 @@ from .tool_choice_apply_patch import ToolChoiceApplyPatch from .response_conversation_param import ResponseConversationParam -__all__ = ["ResponsesClientEvent", "ContextManagement", "Conversation", "StreamOptions", "ToolChoice"] +__all__ = ["ResponsesClientEvent", "ContextManagement", "Conversation", "Moderation", "StreamOptions", "ToolChoice"] class ContextManagement(BaseModel): @@ -36,6 +36,16 @@ class ContextManagement(BaseModel): Conversation: TypeAlias = Union[str, ResponseConversationParam, None] +class Moderation(BaseModel): + """Configuration for running moderation on the input and output of this response.""" + + model: str + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + class StreamOptions(BaseModel): """Options for streaming responses. Only set this when you set `stream: true`.""" @@ -160,6 +170,9 @@ class ResponsesClientEvent(BaseModel): available models. """ + moderation: Optional[Moderation] = None + """Configuration for running moderation on the input and output of this response.""" + parallel_tool_calls: Optional[bool] = None """Whether to allow the model to run tool calls in parallel.""" diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py index 632807aedf..55aeafc33d 100644 --- a/src/openai/types/responses/responses_client_event_param.py +++ b/src/openai/types/responses/responses_client_event_param.py @@ -23,7 +23,14 @@ from ..shared_params.responses_model import ResponsesModel from .response_conversation_param_param import ResponseConversationParamParam -__all__ = ["ResponsesClientEventParam", "ContextManagement", "Conversation", "StreamOptions", "ToolChoice"] +__all__ = [ + "ResponsesClientEventParam", + "ContextManagement", + "Conversation", + "Moderation", + "StreamOptions", + "ToolChoice", +] class ContextManagement(TypedDict, total=False): @@ -37,6 +44,16 @@ class ContextManagement(TypedDict, total=False): Conversation: TypeAlias = Union[str, ResponseConversationParamParam] +class Moderation(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + class StreamOptions(TypedDict, total=False): """Options for streaming responses. Only set this when you set `stream: true`.""" @@ -161,6 +178,9 @@ class ResponsesClientEventParam(TypedDict, total=False): available models. """ + moderation: Optional[Moderation] + """Configuration for running moderation on the input and output of this response.""" + parallel_tool_calls: Optional[bool] """Whether to allow the model to run tool calls in parallel.""" diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index ea3066a505..3cba000ae6 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -65,6 +65,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tokens=0, metadata={"foo": "string"}, modalities=["text"], + moderation={"model": "model"}, n=1, parallel_tool_calls=True, prediction={ @@ -199,6 +200,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tokens=0, metadata={"foo": "string"}, modalities=["text"], + moderation={"model": "model"}, n=1, parallel_tool_calls=True, prediction={ @@ -508,6 +510,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_tokens=0, metadata={"foo": "string"}, modalities=["text"], + moderation={"model": "model"}, n=1, parallel_tool_calls=True, prediction={ @@ -642,6 +645,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_tokens=0, metadata={"foo": "string"}, modalities=["text"], + moderation={"model": "model"}, n=1, parallel_tool_calls=True, prediction={ diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 094687c2c6..dc6083e78c 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -44,6 +44,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", + moderation={"model": "model"}, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -132,6 +133,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", + moderation={"model": "model"}, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -457,6 +459,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", + moderation={"model": "model"}, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -545,6 +548,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", + moderation={"model": "model"}, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ diff --git a/tests/lib/chat/test_completions.py b/tests/lib/chat/test_completions.py index 85bab4f095..741f5eaa75 100644 --- a/tests/lib/chat/test_completions.py +++ b/tests/lib/chat/test_completions.py @@ -72,6 +72,7 @@ def test_parse_nothing(client: OpenAI, respx_mock: MockRouter, monkeypatch: pyte created=1727346142, id='chatcmpl-ABfvaueLEMLNYbT8YzpJxsmiQ6HSY', model='gpt-4o-2024-08-06', + moderation=None, object='chat.completion', service_tier=None, system_fingerprint='fp_b40fb1c6fb', @@ -141,6 +142,7 @@ class Location(BaseModel): created=1727346143, id='chatcmpl-ABfvbtVnTu5DeC4EFnRYj8mtfOM99', model='gpt-4o-2024-08-06', + moderation=None, object='chat.completion', service_tier=None, system_fingerprint='fp_5050236cbd', @@ -212,6 +214,7 @@ class Location(BaseModel): created=1727346144, id='chatcmpl-ABfvcC8grKYsRkSoMp9CCAhbXAd0b', model='gpt-4o-2024-08-06', + moderation=None, object='chat.completion', service_tier=None, system_fingerprint='fp_b40fb1c6fb', @@ -418,6 +421,7 @@ class CalendarEvent: created=1727346158, id='chatcmpl-ABfvqhz4uUUWsw8Ohw2Mp9B4sKKV8', model='gpt-4o-2024-08-06', + moderation=None, object='chat.completion', service_tier=None, system_fingerprint='fp_7568d46099', @@ -887,6 +891,7 @@ class Location(BaseModel): created=1727389540, id='chatcmpl-ABrDYCa8W1w66eUxKDO8TQF1m6trT', model='gpt-4o-2024-08-06', + moderation=None, object='chat.completion', service_tier=None, system_fingerprint='fp_5050236cbd', @@ -964,6 +969,7 @@ class Location(BaseModel): created=1727389532, id='chatcmpl-ABrDQWOiw0PK5JOsxl1D9ooeQgznq', model='gpt-4o-2024-08-06', + moderation=None, object='chat.completion', service_tier=None, system_fingerprint='fp_5050236cbd', diff --git a/tests/lib/chat/test_completions_streaming.py b/tests/lib/chat/test_completions_streaming.py index eb3a0973ac..598a41ee2b 100644 --- a/tests/lib/chat/test_completions_streaming.py +++ b/tests/lib/chat/test_completions_streaming.py @@ -161,6 +161,7 @@ def on_event(stream: ChatCompletionStream[Location], event: ChatCompletionStream created=1727346169, id='chatcmpl-ABfw1e5abtU8OwGr15vOreYVb2MiF', model='gpt-4o-2024-08-06', + moderation=None, object='chat.completion', service_tier=None, system_fingerprint='fp_5050236cbd', From 519cd027919fa5b73bd8fe237e80c7a01b3e0b2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:33:34 +0000 Subject: [PATCH 382/408] release: 2.41.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 69efe8f538..29ad411bac 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.40.0" + ".": "2.41.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d1dc4baba..1a9e138e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.41.0 (2026-06-03) + +Full Changelog: [v2.40.0...v2.41.0](https://github.com/openai/openai-python/compare/v2.40.0...v2.41.0) + +### Features + +* **api:** responses.moderation and chat_completions.moderation ([87e46c2](https://github.com/openai/openai-python/commit/87e46c25ac9ca8cff407b52ad9fb33e326c059d6)) + ## 2.40.0 (2026-06-01) Full Changelog: [v2.39.0...v2.40.0](https://github.com/openai/openai-python/compare/v2.39.0...v2.40.0) diff --git a/pyproject.toml b/pyproject.toml index 37cd1a8db1..d3a6a58a76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.40.0" +version = "2.41.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 7db77fcfff..57fb9f9cca 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.40.0" # x-release-please-version +__version__ = "2.41.0" # x-release-please-version From 2a91011abc21032db9566b98068afefb5fbb9b24 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Fri, 5 Jun 2026 08:50:28 -0700 Subject: [PATCH 383/408] build: Remove scheduled release workflow trigger (#3366) --- .github/workflows/create-releases.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index f39b4c3e2c..7c42b4d6a6 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -1,7 +1,5 @@ name: Create releases on: - schedule: - - cron: '0 5 * * *' # every day at 5am UTC push: branches: - main From 3842a5ea01e8ecb7d6b159b9236c0c60ceff5de3 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Fri, 5 Jun 2026 09:47:36 -0700 Subject: [PATCH 384/408] ci: use PyPI trusted publishing (#3365) --- .github/workflows/create-releases.yml | 55 ++++++++++++++++++++++++--- .github/workflows/publish-pypi.yml | 47 +++++++++++++++++++---- CONTRIBUTING.md | 5 +-- bin/publish-pypi | 6 --- 4 files changed, 91 insertions(+), 22 deletions(-) delete mode 100644 bin/publish-pypi diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 7c42b4d6a6..9d65ef4e8e 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -10,6 +10,10 @@ jobs: if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' runs-on: ubuntu-latest environment: publish + outputs: + releases_created: ${{ steps.release.outputs.releases_created }} + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -20,16 +24,55 @@ jobs: repo: ${{ github.event.repository.full_name }} stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} + build: + name: build + needs: release + if: ${{ needs.release.outputs.releases_created == 'true' }} + runs-on: ubuntu-latest + # Build distributions without OIDC access so package build code cannot mint + # a PyPI publishing token. The publish job handles only the upload. + permissions: + contents: read + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - name: Set up Rye - if: ${{ steps.release.outputs.releases_created }} uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 with: version: '0.44.0' enable-cache: true - - name: Publish to PyPI - if: ${{ steps.release.outputs.releases_created }} + - name: Build package run: | - bash ./bin/publish-pypi - env: - PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} + mkdir -p dist + rye build --clean + + - name: Upload package distributions + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: python-package-distributions + path: dist/ + if-no-files-found: error + retention-days: 1 + + publish: + name: publish + needs: build + runs-on: ubuntu-latest + environment: publish + # PyPI Trusted Publishing requires id-token: write. Keep it scoped to this + # minimal upload-only job rather than the build job. + permissions: + contents: read + id-token: write + + steps: + - name: Download package distributions + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: python-package-distributions + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index a7c62c4c4d..d23cd66942 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -5,10 +5,14 @@ on: workflow_dispatch: jobs: - publish: - name: publish + build: + name: build + if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' runs-on: ubuntu-latest - environment: publish + # Build distributions without OIDC access so package build code cannot mint + # a PyPI publishing token. The publish job handles only the upload. + permissions: + contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -19,8 +23,37 @@ jobs: version: '0.44.0' enable-cache: true - - name: Publish to PyPI + - name: Build package run: | - bash ./bin/publish-pypi - env: - PYPI_TOKEN: ${{ secrets.OPENAI_PYPI_TOKEN || secrets.PYPI_TOKEN }} + mkdir -p dist + rye build --clean + + - name: Upload package distributions + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: python-package-distributions + path: dist/ + if-no-files-found: error + retention-days: 1 + + publish: + name: publish + needs: build + if: github.ref == 'refs/heads/main' && github.repository == 'openai/openai-python' + runs-on: ubuntu-latest + environment: publish + # PyPI Trusted Publishing requires id-token: write. Keep it scoped to this + # minimal upload-only job rather than the build job. + permissions: + contents: read + id-token: write + + steps: + - name: Download package distributions + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: python-package-distributions + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 253b9ce5e6..f92377d459 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,9 +119,8 @@ the changes aren't made through the automated pipeline, you may want to make rel ### Publish with a GitHub workflow -You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/openai/openai-python/actions/workflows/publish-pypi.yml). PyPI publishing uses Trusted Publishing, so the PyPI project must trust this repository's GitHub Actions workflow and the `publish` environment. ### Publish manually -If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on -the environment. +If you need to retry a PyPI release, use the `Publish PyPI` GitHub action. Local manual publishing is not the standard release path because the GitHub workflow uses OIDC instead of a long-lived PyPI token. diff --git a/bin/publish-pypi b/bin/publish-pypi deleted file mode 100644 index 826054e924..0000000000 --- a/bin/publish-pypi +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -eux -mkdir -p dist -rye build --clean -rye publish --yes --token=$PYPI_TOKEN From 71987563c833806bb9854c2f74d430a26e319246 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 15:51:40 +0000 Subject: [PATCH 385/408] release: 2.41.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 29ad411bac..25d9dabb53 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.41.0" + ".": "2.41.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a9e138e0b..1a2a2e0b16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.41.1 (2026-06-05) + +Full Changelog: [v2.41.0...v2.41.1](https://github.com/openai/openai-python/compare/v2.41.0...v2.41.1) + +### Build System + +* Remove scheduled release workflow trigger ([#3366](https://github.com/openai/openai-python/issues/3366)) ([2a91011](https://github.com/openai/openai-python/commit/2a91011abc21032db9566b98068afefb5fbb9b24)) + ## 2.41.0 (2026-06-03) Full Changelog: [v2.40.0...v2.41.0](https://github.com/openai/openai-python/compare/v2.40.0...v2.41.0) diff --git a/pyproject.toml b/pyproject.toml index d3a6a58a76..75d0d5e246 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.41.0" +version = "2.41.1" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 57fb9f9cca..55f1df75a9 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.41.0" # x-release-please-version +__version__ = "2.41.1" # x-release-please-version From a526ee813f085318fe3c6923ac3fa10c1cf56420 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Wed, 10 Jun 2026 09:09:14 -0700 Subject: [PATCH 386/408] build: fix release workflow permissions (#3389) --- .github/workflows/create-releases.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-releases.yml b/.github/workflows/create-releases.yml index 9d65ef4e8e..fc38031b96 100644 --- a/.github/workflows/create-releases.yml +++ b/.github/workflows/create-releases.yml @@ -13,7 +13,7 @@ jobs: outputs: releases_created: ${{ steps.release.outputs.releases_created }} permissions: - contents: read + contents: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 From d64d811e82aff724397e32d593e50657fee3f905 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Thu, 11 Jun 2026 12:52:41 -0700 Subject: [PATCH 387/408] build: Use CI environment for examples API key (#3394) --- .github/workflows/ci.yml | 1 + examples/async_demo.py | 18 +++++++++++++----- examples/demo.py | 6 +++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 527e036a0e..9166b6c8b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,7 @@ jobs: examples: timeout-minutes: 10 name: examples + environment: ci runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') diff --git a/examples/async_demo.py b/examples/async_demo.py index 793b4e43fb..438706da44 100755 --- a/examples/async_demo.py +++ b/examples/async_demo.py @@ -9,13 +9,21 @@ async def main() -> None: - stream = await client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="Say this is a test", + stream = await client.chat.completions.create( + model="gpt-5.5", + messages=[ + { + "role": "user", + "content": "Say this is a test", + }, + ], stream=True, ) - async for completion in stream: - print(completion.choices[0].text, end="") + async for chunk in stream: + if not chunk.choices: + continue + + print(chunk.choices[0].delta.content, end="") print() diff --git a/examples/demo.py b/examples/demo.py index ac1710f3e0..e67b276deb 100755 --- a/examples/demo.py +++ b/examples/demo.py @@ -8,7 +8,7 @@ # Non-streaming: print("----- standard request -----") completion = client.chat.completions.create( - model="gpt-4", + model="gpt-5.5", messages=[ { "role": "user", @@ -21,7 +21,7 @@ # Streaming: print("----- streaming request -----") stream = client.chat.completions.create( - model="gpt-4", + model="gpt-5.5", messages=[ { "role": "user", @@ -40,7 +40,7 @@ # Response headers: print("----- custom response headers test -----") response = client.chat.completions.with_raw_response.create( - model="gpt-4", + model="gpt-5.5", messages=[ { "role": "user", From 60002e574925e84261150f3e01be4bcb53658261 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 19:55:23 +0000 Subject: [PATCH 388/408] feat(api): update OpenAPI spec or Stainless config --- .stats.yml | 4 +- .../admin/organization/admin_api_keys.py | 24 ++++- .../admin/organization/audit_logs.py | 24 ++++- .../types/admin/organization/admin_api_key.py | 3 + .../admin_api_key_create_params.py | 6 ++ .../organization/audit_log_list_params.py | 14 ++- .../organization/audit_log_list_response.py | 96 +++++++++++++++++++ src/openai/types/responses/response.py | 24 +++-- src/openai/types/shared/reasoning.py | 7 ++ src/openai/types/shared_params/reasoning.py | 7 ++ src/openai/types/video_edit_params.py | 2 +- .../admin/organization/test_admin_api_keys.py | 16 ++++ .../admin/organization/test_audit_logs.py | 2 + .../responses/test_input_tokens.py | 2 + tests/api_resources/test_responses.py | 4 + 15 files changed, 221 insertions(+), 14 deletions(-) diff --git a/.stats.yml b/.stats.yml index a62e7b979c..ea980d413b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 262 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0161cb2a0faadedfc17ff59c7fcad9671962dbd170f83811003c23702148cf36.yml -openapi_spec_hash: 1023c7442d0e2e7e213e8b3a253921c5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-e00ba6273043ec67eab4c213e30df144731fc5ae51d7c820ea647990c552b60b.yml +openapi_spec_hash: 9168743f8e60b63630e679583b29a840 config_hash: e02ca1082421dfe55b145c45e95d6126 diff --git a/src/openai/resources/admin/organization/admin_api_keys.py b/src/openai/resources/admin/organization/admin_api_keys.py index 80ddc39b49..fe10e236f5 100644 --- a/src/openai/resources/admin/organization/admin_api_keys.py +++ b/src/openai/resources/admin/organization/admin_api_keys.py @@ -47,6 +47,7 @@ def create( self, *, name: str, + expires_in_seconds: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -58,6 +59,9 @@ def create( Create an organization admin API key Args: + expires_in_seconds: The number of seconds until the API key expires. Omit this field for a key that + does not expire. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -68,7 +72,13 @@ def create( """ return self._post( "/organization/admin_api_keys", - body=maybe_transform({"name": name}, admin_api_key_create_params.AdminAPIKeyCreateParams), + body=maybe_transform( + { + "name": name, + "expires_in_seconds": expires_in_seconds, + }, + admin_api_key_create_params.AdminAPIKeyCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -234,6 +244,7 @@ async def create( self, *, name: str, + expires_in_seconds: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -245,6 +256,9 @@ async def create( Create an organization admin API key Args: + expires_in_seconds: The number of seconds until the API key expires. Omit this field for a key that + does not expire. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -255,7 +269,13 @@ async def create( """ return await self._post( "/organization/admin_api_keys", - body=await async_maybe_transform({"name": name}, admin_api_key_create_params.AdminAPIKeyCreateParams), + body=await async_maybe_transform( + { + "name": name, + "expires_in_seconds": expires_in_seconds, + }, + admin_api_key_create_params.AdminAPIKeyCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py index 760fb4b034..1774a80aed 100644 --- a/src/openai/resources/admin/organization/audit_logs.py +++ b/src/openai/resources/admin/organization/audit_logs.py @@ -102,6 +102,8 @@ def list( "role.deleted", "role.assignment.created", "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", "scim.enabled", "scim.disabled", "service_account.created", @@ -116,6 +118,7 @@ def list( limit: int | Omit = omit, project_ids: SequenceNotStr[str] | Omit = omit, resource_ids: SequenceNotStr[str] | Omit = omit, + tenant_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -154,7 +157,13 @@ def list( project_ids: Return only events for these projects. resource_ids: Return only events performed on these targets. For example, a project ID - updated. + updated. For ChatGPT connector role events, use the workspace connector resource + ID shown in `details.id`, such as `__`. + + tenant_only: Return only tenant-scoped events associated with this organization. Required for + tenant-scoped events such as `role.bound_to_resource` and + `role.unbound_from_resource`. When `true`, all supplied event types must be + tenant-scoped. extra_headers: Send extra headers @@ -183,6 +192,7 @@ def list( "limit": limit, "project_ids": project_ids, "resource_ids": resource_ids, + "tenant_only": tenant_only, }, audit_log_list_params.AuditLogListParams, ), @@ -273,6 +283,8 @@ def list( "role.deleted", "role.assignment.created", "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", "scim.enabled", "scim.disabled", "service_account.created", @@ -287,6 +299,7 @@ def list( limit: int | Omit = omit, project_ids: SequenceNotStr[str] | Omit = omit, resource_ids: SequenceNotStr[str] | Omit = omit, + tenant_only: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -325,7 +338,13 @@ def list( project_ids: Return only events for these projects. resource_ids: Return only events performed on these targets. For example, a project ID - updated. + updated. For ChatGPT connector role events, use the workspace connector resource + ID shown in `details.id`, such as `__`. + + tenant_only: Return only tenant-scoped events associated with this organization. Required for + tenant-scoped events such as `role.bound_to_resource` and + `role.unbound_from_resource`. When `true`, all supplied event types must be + tenant-scoped. extra_headers: Send extra headers @@ -354,6 +373,7 @@ def list( "limit": limit, "project_ids": project_ids, "resource_ids": resource_ids, + "tenant_only": tenant_only, }, audit_log_list_params.AuditLogListParams, ), diff --git a/src/openai/types/admin/organization/admin_api_key.py b/src/openai/types/admin/organization/admin_api_key.py index 5d2e5840f9..b9323dd465 100644 --- a/src/openai/types/admin/organization/admin_api_key.py +++ b/src/openai/types/admin/organization/admin_api_key.py @@ -37,6 +37,9 @@ class AdminAPIKey(BaseModel): created_at: int """The Unix timestamp (in seconds) of when the API key was created""" + expires_at: Optional[int] = None + """The Unix timestamp (in seconds) of when the API key expires""" + object: Literal["organization.admin_api_key"] """The object type, which is always `organization.admin_api_key`""" diff --git a/src/openai/types/admin/organization/admin_api_key_create_params.py b/src/openai/types/admin/organization/admin_api_key_create_params.py index dccdfb8a75..ac7b22ea30 100644 --- a/src/openai/types/admin/organization/admin_api_key_create_params.py +++ b/src/openai/types/admin/organization/admin_api_key_create_params.py @@ -9,3 +9,9 @@ class AdminAPIKeyCreateParams(TypedDict, total=False): name: Required[str] + + expires_in_seconds: int + """The number of seconds until the API key expires. + + Omit this field for a key that does not expire. + """ diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py index 25224d47dc..e77e1c8d96 100644 --- a/src/openai/types/admin/organization/audit_log_list_params.py +++ b/src/openai/types/admin/organization/audit_log_list_params.py @@ -92,6 +92,8 @@ class AuditLogListParams(TypedDict, total=False): "role.deleted", "role.assignment.created", "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", "scim.enabled", "scim.disabled", "service_account.created", @@ -120,7 +122,17 @@ class AuditLogListParams(TypedDict, total=False): resource_ids: SequenceNotStr[str] """Return only events performed on these targets. - For example, a project ID updated. + For example, a project ID updated. For ChatGPT connector role events, use the + workspace connector resource ID shown in `details.id`, such as + `__`. + """ + + tenant_only: bool + """Return only tenant-scoped events associated with this organization. + + Required for tenant-scoped events such as `role.bound_to_resource` and + `role.unbound_from_resource`. When `true`, all supplied event types must be + tenant-scoped. """ diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py index 9fa99bd677..f2f63c2e74 100644 --- a/src/openai/types/admin/organization/audit_log_list_response.py +++ b/src/openai/types/admin/organization/audit_log_list_response.py @@ -64,8 +64,10 @@ "RateLimitUpdatedChangesRequested", "RoleAssignmentCreated", "RoleAssignmentDeleted", + "RoleBoundToResource", "RoleCreated", "RoleDeleted", + "RoleUnboundFromResource", "RoleUpdated", "RoleUpdatedChangesRequested", "ScimDisabled", @@ -653,6 +655,48 @@ class RoleAssignmentDeleted(BaseModel): """The type of resource the role assignment was scoped to.""" +class RoleBoundToResource(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the resource the role was bound to. + + ChatGPT workspace connector resources use `__`. + """ + + connector_id: Optional[str] = None + """The connector ID for a ChatGPT workspace connector resource.""" + + connector_name: Optional[str] = None + """ + The connector display name for a ChatGPT workspace connector resource, or the + connector ID when the display name could not be resolved. + """ + + enabled: Optional[bool] = None + """Whether the connector is enabled for the role.""" + + permissions: Optional[List[str]] = None + """The permissions granted to the role for the resource.""" + + resource_id: Optional[str] = None + """The ID of the resource the role was bound to.""" + + resource_type: Optional[str] = None + """The type of resource the role was bound to.""" + + role_id: Optional[str] = None + """The ID of the role that was bound to the resource.""" + + source: Optional[ + Literal["role_toggle", "role_connector_update", "role_delete", "workspace_permissions", "connector_publish"] + ] = None + """The connector role mutation path that produced the event.""" + + workspace_id: Optional[str] = None + """The workspace ID for a ChatGPT workspace connector resource.""" + + class RoleCreated(BaseModel): """The details for events with this `type`.""" @@ -679,6 +723,48 @@ class RoleDeleted(BaseModel): """The role ID.""" +class RoleUnboundFromResource(BaseModel): + """The details for events with this `type`.""" + + id: Optional[str] = None + """The ID of the resource the role was unbound from. + + ChatGPT workspace connector resources use `__`. + """ + + connector_id: Optional[str] = None + """The connector ID for a ChatGPT workspace connector resource.""" + + connector_name: Optional[str] = None + """ + The connector display name for a ChatGPT workspace connector resource, or the + connector ID when the display name could not be resolved. + """ + + enabled: Optional[bool] = None + """Whether the connector is enabled for the role.""" + + permissions: Optional[List[str]] = None + """The permissions remaining for the role after the change.""" + + resource_id: Optional[str] = None + """The ID of the resource the role was unbound from.""" + + resource_type: Optional[str] = None + """The type of resource the role was unbound from.""" + + role_id: Optional[str] = None + """The ID of the role that was unbound from the resource.""" + + source: Optional[ + Literal["role_toggle", "role_connector_update", "role_delete", "workspace_permissions", "connector_publish"] + ] = None + """The connector role mutation path that produced the event.""" + + workspace_id: Optional[str] = None + """The workspace ID for a ChatGPT workspace connector resource.""" + + class RoleUpdatedChangesRequested(BaseModel): """The payload used to update the role.""" @@ -941,6 +1027,8 @@ class AuditLogListResponse(BaseModel): "role.deleted", "role.assignment.created", "role.assignment.deleted", + "role.bound_to_resource", + "role.unbound_from_resource", "scim.enabled", "scim.disabled", "service_account.created", @@ -1083,12 +1171,20 @@ class AuditLogListResponse(BaseModel): role_assignment_deleted: Optional[RoleAssignmentDeleted] = FieldInfo(alias="role.assignment.deleted", default=None) """The details for events with this `type`.""" + role_bound_to_resource: Optional[RoleBoundToResource] = FieldInfo(alias="role.bound_to_resource", default=None) + """The details for events with this `type`.""" + role_created: Optional[RoleCreated] = FieldInfo(alias="role.created", default=None) """The details for events with this `type`.""" role_deleted: Optional[RoleDeleted] = FieldInfo(alias="role.deleted", default=None) """The details for events with this `type`.""" + role_unbound_from_resource: Optional[RoleUnboundFromResource] = FieldInfo( + alias="role.unbound_from_resource", default=None + ) + """The details for events with this `type`.""" + role_updated: Optional[RoleUpdated] = FieldInfo(alias="role.updated", default=None) """The details for events with this `type`.""" diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 67102d2628..e69aae9ad4 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -12,7 +12,6 @@ from .response_status import ResponseStatus from .tool_choice_mcp import ToolChoiceMcp from ..shared.metadata import Metadata -from ..shared.reasoning import Reasoning from .tool_choice_shell import ToolChoiceShell from .tool_choice_types import ToolChoiceTypes from .tool_choice_custom import ToolChoiceCustom @@ -37,6 +36,7 @@ "ModerationOutput", "ModerationOutputModerationResult", "ModerationOutputError", + "Reasoning", ] @@ -173,6 +173,22 @@ class Moderation(BaseModel): """Moderation for the response output.""" +class Reasoning(BaseModel): + """Reasoning configuration and metadata that were used for the response.""" + + context: Optional[Literal["current_turn", "all_turns"]] = None + """The effective reasoning context mode used for the response.""" + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None + """The reasoning effort that was requested for the model, if specified.""" + + summary: Optional[Literal["concise", "detailed", "auto"]] = None + """A model-generated summary of its reasoning that was produced, if available.""" + + generate_summary: Optional[Literal["concise", "detailed", "auto"]] = None + """Deprecated. `summary` was used instead.""" + + class Response(BaseModel): id: str """Unique identifier for this Response.""" @@ -354,11 +370,7 @@ class Response(BaseModel): """ reasoning: Optional[Reasoning] = None - """**gpt-5 and o-series models only** - - Configuration options for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). - """ + """Reasoning configuration and metadata that were used for the response.""" safety_identifier: Optional[str] = None """ diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 14f56a04cd..6a7b3e4b7d 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -16,6 +16,13 @@ class Reasoning(BaseModel): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ + context: Optional[Literal["auto", "current_turn", "all_turns"]] = None + """ + Controls which reasoning items are rendered back to the model on later turns. + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + effort: Optional[ReasoningEffort] = None """ Constrains effort on reasoning for diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 2bd7ce7268..86ebc259f1 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -17,6 +17,13 @@ class Reasoning(TypedDict, total=False): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + effort: Optional[ReasoningEffort] """ Constrains effort on reasoning for diff --git a/src/openai/types/video_edit_params.py b/src/openai/types/video_edit_params.py index 8d3b15fc6f..44e8c35d7f 100644 --- a/src/openai/types/video_edit_params.py +++ b/src/openai/types/video_edit_params.py @@ -19,7 +19,7 @@ class VideoEditParams(TypedDict, total=False): class VideoVideoReferenceInputParam(TypedDict, total=False): - """Reference to the completed video.""" + """Reference to the completed video to edit.""" id: Required[str] """The identifier of the completed video.""" diff --git a/tests/api_resources/admin/organization/test_admin_api_keys.py b/tests/api_resources/admin/organization/test_admin_api_keys.py index 59eed910e8..3384ee32df 100644 --- a/tests/api_resources/admin/organization/test_admin_api_keys.py +++ b/tests/api_resources/admin/organization/test_admin_api_keys.py @@ -29,6 +29,14 @@ def test_method_create(self, client: OpenAI) -> None: ) assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + admin_api_key = client.admin.organization.admin_api_keys.create( + name="New Admin Key", + expires_in_seconds=2592000, + ) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) + @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.admin.organization.admin_api_keys.with_raw_response.create( @@ -176,6 +184,14 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + admin_api_key = await async_client.admin.organization.admin_api_keys.create( + name="New Admin Key", + expires_in_seconds=2592000, + ) + assert_matches_type(AdminAPIKeyCreateResponse, admin_api_key, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.admin_api_keys.with_raw_response.create( diff --git a/tests/api_resources/admin/organization/test_audit_logs.py b/tests/api_resources/admin/organization/test_audit_logs.py index 5e696461fa..cf60c407b3 100644 --- a/tests/api_resources/admin/organization/test_audit_logs.py +++ b/tests/api_resources/admin/organization/test_audit_logs.py @@ -40,6 +40,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: limit=0, project_ids=["string"], resource_ids=["string"], + tenant_only=True, ) assert_matches_type(SyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) @@ -91,6 +92,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N limit=0, project_ids=["string"], resource_ids=["string"], + tenant_only=True, ) assert_matches_type(AsyncConversationCursorPage[AuditLogListResponse], audit_log, path=["response"]) diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py index 04c0bb698e..59ff4252b9 100644 --- a/tests/api_resources/responses/test_input_tokens.py +++ b/tests/api_resources/responses/test_input_tokens.py @@ -33,6 +33,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None: personality="friendly", previous_response_id="resp_123", reasoning={ + "context": "auto", "effort": "none", "generate_summary": "auto", "summary": "auto", @@ -98,6 +99,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> personality="friendly", previous_response_id="resp_123", reasoning={ + "context": "auto", "effort": "none", "generate_summary": "auto", "summary": "auto", diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index dc6083e78c..761b410943 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -55,6 +55,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: prompt_cache_key="prompt-cache-key-1234", prompt_cache_retention="in_memory", reasoning={ + "context": "auto", "effort": "none", "generate_summary": "auto", "summary": "auto", @@ -144,6 +145,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: prompt_cache_key="prompt-cache-key-1234", prompt_cache_retention="in_memory", reasoning={ + "context": "auto", "effort": "none", "generate_summary": "auto", "summary": "auto", @@ -470,6 +472,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn prompt_cache_key="prompt-cache-key-1234", prompt_cache_retention="in_memory", reasoning={ + "context": "auto", "effort": "none", "generate_summary": "auto", "summary": "auto", @@ -559,6 +562,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn prompt_cache_key="prompt-cache-key-1234", prompt_cache_retention="in_memory", reasoning={ + "context": "auto", "effort": "none", "generate_summary": "auto", "summary": "auto", From ee821d6b4a59d6b73850640162b6454da434f5f7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:28:25 +0000 Subject: [PATCH 389/408] feat(api): admin spend_alerts --- .stats.yml | 8 +- api.md | 2 + .../organization/projects/spend_alerts.py | 96 +++++++++++++++++++ .../admin/organization/spend_alerts.py | 86 +++++++++++++++++ src/openai/types/responses/response.py | 24 ++--- .../projects/test_spend_alerts.py | 96 +++++++++++++++++++ .../admin/organization/test_spend_alerts.py | 76 +++++++++++++++ 7 files changed, 366 insertions(+), 22 deletions(-) diff --git a/.stats.yml b/.stats.yml index ea980d413b..b1a3597bc6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 262 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-e00ba6273043ec67eab4c213e30df144731fc5ae51d7c820ea647990c552b60b.yml -openapi_spec_hash: 9168743f8e60b63630e679583b29a840 -config_hash: e02ca1082421dfe55b145c45e95d6126 +configured_endpoints: 264 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-c16aadb4992b72ce2391b8db90427e6be355e6c5f3f372bad3bfad7de5b8c8ed.yml +openapi_spec_hash: 310aad77be43c413a991e659beedc1a8 +config_hash: c60fedbe1462084139674a3c34bbbc95 diff --git a/api.md b/api.md index 10a729d995..56e01254dc 100644 --- a/api.md +++ b/api.md @@ -922,6 +922,7 @@ from openai.types.admin.organization import OrganizationSpendAlert, Organization Methods: - client.admin.organization.spend_alerts.create(\*\*params) -> OrganizationSpendAlert +- client.admin.organization.spend_alerts.retrieve(alert_id) -> OrganizationSpendAlert - client.admin.organization.spend_alerts.update(alert_id, \*\*params) -> OrganizationSpendAlert - client.admin.organization.spend_alerts.list(\*\*params) -> SyncConversationCursorPage[OrganizationSpendAlert] - client.admin.organization.spend_alerts.delete(alert_id) -> OrganizationSpendAlertDeleted @@ -1154,6 +1155,7 @@ from openai.types.admin.organization.projects import ProjectSpendAlert, ProjectS Methods: - client.admin.organization.projects.spend_alerts.create(project_id, \*\*params) -> ProjectSpendAlert +- client.admin.organization.projects.spend_alerts.retrieve(alert_id, \*, project_id) -> ProjectSpendAlert - client.admin.organization.projects.spend_alerts.update(alert_id, \*, project_id, \*\*params) -> ProjectSpendAlert - client.admin.organization.projects.spend_alerts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectSpendAlert] - client.admin.organization.projects.spend_alerts.delete(alert_id, \*, project_id) -> ProjectSpendAlertDeleted diff --git a/src/openai/resources/admin/organization/projects/spend_alerts.py b/src/openai/resources/admin/organization/projects/spend_alerts.py index 3bc5d0c1f9..9eb0922df5 100644 --- a/src/openai/resources/admin/organization/projects/spend_alerts.py +++ b/src/openai/resources/admin/organization/projects/spend_alerts.py @@ -103,6 +103,48 @@ def create( cast_to=ProjectSpendAlert, ) + def retrieve( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Retrieves a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._get( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + def update( self, alert_id: str, @@ -349,6 +391,48 @@ async def create( cast_to=ProjectSpendAlert, ) + async def retrieve( + self, + alert_id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendAlert: + """ + Retrieves a project spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._get( + path_template( + "/organization/projects/{project_id}/spend_alerts/{alert_id}", project_id=project_id, alert_id=alert_id + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendAlert, + ) + async def update( self, alert_id: str, @@ -524,6 +608,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None: self.create = _legacy_response.to_raw_response_wrapper( spend_alerts.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_alerts.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( spend_alerts.update, ) @@ -542,6 +629,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( spend_alerts.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( spend_alerts.update, ) @@ -560,6 +650,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None: self.create = to_streamed_response_wrapper( spend_alerts.create, ) + self.retrieve = to_streamed_response_wrapper( + spend_alerts.retrieve, + ) self.update = to_streamed_response_wrapper( spend_alerts.update, ) @@ -578,6 +671,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: self.create = async_to_streamed_response_wrapper( spend_alerts.create, ) + self.retrieve = async_to_streamed_response_wrapper( + spend_alerts.retrieve, + ) self.update = async_to_streamed_response_wrapper( spend_alerts.update, ) diff --git a/src/openai/resources/admin/organization/spend_alerts.py b/src/openai/resources/admin/organization/spend_alerts.py index 4f3e223269..0da46176b6 100644 --- a/src/openai/resources/admin/organization/spend_alerts.py +++ b/src/openai/resources/admin/organization/spend_alerts.py @@ -96,6 +96,43 @@ def create( cast_to=OrganizationSpendAlert, ) + def retrieve( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Retrieves an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return self._get( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + def update( self, alert_id: str, @@ -326,6 +363,43 @@ async def create( cast_to=OrganizationSpendAlert, ) + async def retrieve( + self, + alert_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendAlert: + """ + Retrieves an organization spend alert. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not alert_id: + raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") + return await self._get( + path_template("/organization/spend_alerts/{alert_id}", alert_id=alert_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendAlert, + ) + async def update( self, alert_id: str, @@ -488,6 +562,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None: self.create = _legacy_response.to_raw_response_wrapper( spend_alerts.create, ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_alerts.retrieve, + ) self.update = _legacy_response.to_raw_response_wrapper( spend_alerts.update, ) @@ -506,6 +583,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: self.create = _legacy_response.async_to_raw_response_wrapper( spend_alerts.create, ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_alerts.retrieve, + ) self.update = _legacy_response.async_to_raw_response_wrapper( spend_alerts.update, ) @@ -524,6 +604,9 @@ def __init__(self, spend_alerts: SpendAlerts) -> None: self.create = to_streamed_response_wrapper( spend_alerts.create, ) + self.retrieve = to_streamed_response_wrapper( + spend_alerts.retrieve, + ) self.update = to_streamed_response_wrapper( spend_alerts.update, ) @@ -542,6 +625,9 @@ def __init__(self, spend_alerts: AsyncSpendAlerts) -> None: self.create = async_to_streamed_response_wrapper( spend_alerts.create, ) + self.retrieve = async_to_streamed_response_wrapper( + spend_alerts.retrieve, + ) self.update = async_to_streamed_response_wrapper( spend_alerts.update, ) diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index e69aae9ad4..67102d2628 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -12,6 +12,7 @@ from .response_status import ResponseStatus from .tool_choice_mcp import ToolChoiceMcp from ..shared.metadata import Metadata +from ..shared.reasoning import Reasoning from .tool_choice_shell import ToolChoiceShell from .tool_choice_types import ToolChoiceTypes from .tool_choice_custom import ToolChoiceCustom @@ -36,7 +37,6 @@ "ModerationOutput", "ModerationOutputModerationResult", "ModerationOutputError", - "Reasoning", ] @@ -173,22 +173,6 @@ class Moderation(BaseModel): """Moderation for the response output.""" -class Reasoning(BaseModel): - """Reasoning configuration and metadata that were used for the response.""" - - context: Optional[Literal["current_turn", "all_turns"]] = None - """The effective reasoning context mode used for the response.""" - - effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] = None - """The reasoning effort that was requested for the model, if specified.""" - - summary: Optional[Literal["concise", "detailed", "auto"]] = None - """A model-generated summary of its reasoning that was produced, if available.""" - - generate_summary: Optional[Literal["concise", "detailed", "auto"]] = None - """Deprecated. `summary` was used instead.""" - - class Response(BaseModel): id: str """Unique identifier for this Response.""" @@ -370,7 +354,11 @@ class Response(BaseModel): """ reasoning: Optional[Reasoning] = None - """Reasoning configuration and metadata that were used for the response.""" + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ safety_identifier: Optional[str] = None """ diff --git a/tests/api_resources/admin/organization/projects/test_spend_alerts.py b/tests/api_resources/admin/organization/projects/test_spend_alerts.py index c763401af3..ee53d014dd 100644 --- a/tests/api_resources/admin/organization/projects/test_spend_alerts.py +++ b/tests/api_resources/admin/organization/projects/test_spend_alerts.py @@ -102,6 +102,54 @@ def test_path_params_create(self, client: OpenAI) -> None: threshold_amount=0, ) + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.projects.spend_alerts.retrieve( + alert_id="alert_id", + project_id="project_id", + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_alerts.with_raw_response.retrieve( + alert_id="alert_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_alerts.with_streaming_response.retrieve( + alert_id="alert_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.retrieve( + alert_id="alert_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.projects.spend_alerts.with_raw_response.retrieve( + alert_id="", + project_id="project_id", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: spend_alert = client.admin.organization.projects.spend_alerts.update( @@ -385,6 +433,54 @@ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: threshold_amount=0, ) + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.projects.spend_alerts.retrieve( + alert_id="alert_id", + project_id="project_id", + ) + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_alerts.with_raw_response.retrieve( + alert_id="alert_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_alerts.with_streaming_response.retrieve( + alert_id="alert_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(ProjectSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.retrieve( + alert_id="alert_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.projects.spend_alerts.with_raw_response.retrieve( + alert_id="", + project_id="project_id", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: spend_alert = await async_client.admin.organization.projects.spend_alerts.update( diff --git a/tests/api_resources/admin/organization/test_spend_alerts.py b/tests/api_resources/admin/organization/test_spend_alerts.py index 08a3c445c3..13e80d7afa 100644 --- a/tests/api_resources/admin/organization/test_spend_alerts.py +++ b/tests/api_resources/admin/organization/test_spend_alerts.py @@ -84,6 +84,44 @@ def test_streaming_response_create(self, client: OpenAI) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + spend_alert = client.admin.organization.spend_alerts.retrieve( + "alert_id", + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.spend_alerts.with_raw_response.retrieve( + "alert_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.spend_alerts.with_streaming_response.retrieve( + "alert_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + client.admin.organization.spend_alerts.with_raw_response.retrieve( + "", + ) + @parametrize def test_method_update(self, client: OpenAI) -> None: spend_alert = client.admin.organization.spend_alerts.update( @@ -307,6 +345,44 @@ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> Non assert cast(Any, response.is_closed) is True + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + spend_alert = await async_client.admin.organization.spend_alerts.retrieve( + "alert_id", + ) + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_alerts.with_raw_response.retrieve( + "alert_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_alert = response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_alerts.with_streaming_response.retrieve( + "alert_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_alert = await response.parse() + assert_matches_type(OrganizationSpendAlert, spend_alert, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `alert_id` but received ''"): + await async_client.admin.organization.spend_alerts.with_raw_response.retrieve( + "", + ) + @parametrize async def test_method_update(self, async_client: AsyncOpenAI) -> None: spend_alert = await async_client.admin.organization.spend_alerts.update( From b269f88a30fcfbcceea1cc30deb00405db88c5e6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:30:11 +0000 Subject: [PATCH 390/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index b1a3597bc6..db5da516b8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 264 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-c16aadb4992b72ce2391b8db90427e6be355e6c5f3f372bad3bfad7de5b8c8ed.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-3b6617316ec90f4c2426d20b137184a090adf479265d36613222b5f58b4faa97.yml openapi_spec_hash: 310aad77be43c413a991e659beedc1a8 -config_hash: c60fedbe1462084139674a3c34bbbc95 +config_hash: bb963cf2eb4f9a31afd86839949ac24c From 93c2eca17d2d484b95245c849b39f51c6d291478 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:38:30 +0000 Subject: [PATCH 391/408] feat(api): manual updates --- .stats.yml | 6 +++--- src/openai/types/video_edit_params.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index db5da516b8..fef3ddfe2f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 264 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-3b6617316ec90f4c2426d20b137184a090adf479265d36613222b5f58b4faa97.yml -openapi_spec_hash: 310aad77be43c413a991e659beedc1a8 -config_hash: bb963cf2eb4f9a31afd86839949ac24c +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-1d7b3ad7ffe7acee54d8097b85de7f335b8dec979e10ed1780ba07da7fbe78c0.yml +openapi_spec_hash: 2466f6ad496b27334217999202e185c0 +config_hash: 2895eaa2c8a5cb56f29eb33e5feaac1a diff --git a/src/openai/types/video_edit_params.py b/src/openai/types/video_edit_params.py index 44e8c35d7f..8d3b15fc6f 100644 --- a/src/openai/types/video_edit_params.py +++ b/src/openai/types/video_edit_params.py @@ -19,7 +19,7 @@ class VideoEditParams(TypedDict, total=False): class VideoVideoReferenceInputParam(TypedDict, total=False): - """Reference to the completed video to edit.""" + """Reference to the completed video.""" id: Required[str] """The identifier of the completed video.""" From 1ec3e8298982d900d31b486177b5d4cecadcf035 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:44:29 +0000 Subject: [PATCH 392/408] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index fef3ddfe2f..693dbe3631 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 264 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-1d7b3ad7ffe7acee54d8097b85de7f335b8dec979e10ed1780ba07da7fbe78c0.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-4db8edc05bd5503d4aaad74e8b9c783aa1f4d40382a8075c53355bd18dfaf68c.yml openapi_spec_hash: 2466f6ad496b27334217999202e185c0 -config_hash: 2895eaa2c8a5cb56f29eb33e5feaac1a +config_hash: ef3ce17315a31703e7af0567b3e9738c From 2bba15e71817a75e9b8517e09cb244bc144b50d0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:45:23 +0000 Subject: [PATCH 393/408] release: 2.42.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 16 ++++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 25d9dabb53..c33eb8cbe1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.41.1" + ".": "2.42.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a2a2e0b16..ed57a6ca21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 2.42.0 (2026-06-16) + +Full Changelog: [v2.41.1...v2.42.0](https://github.com/openai/openai-python/compare/v2.41.1...v2.42.0) + +### Features + +* **api:** admin spend_alerts ([6134198](https://github.com/openai/openai-python/commit/6134198a488996c4ff6fca4551afd55fb3294fdc)) +* **api:** manual updates ([f337bf4](https://github.com/openai/openai-python/commit/f337bf43276c880d2daf09a5d7f9fc9a886c4bf2)) +* **api:** update OpenAPI spec or Stainless config ([7015158](https://github.com/openai/openai-python/commit/7015158c3119acf57af6c20903587cef928530a9)) + + +### Build System + +* fix release workflow permissions ([#3389](https://github.com/openai/openai-python/issues/3389)) ([a526ee8](https://github.com/openai/openai-python/commit/a526ee813f085318fe3c6923ac3fa10c1cf56420)) +* Use CI environment for examples API key ([#3394](https://github.com/openai/openai-python/issues/3394)) ([d64d811](https://github.com/openai/openai-python/commit/d64d811e82aff724397e32d593e50657fee3f905)) + ## 2.41.1 (2026-06-05) Full Changelog: [v2.41.0...v2.41.1](https://github.com/openai/openai-python/compare/v2.41.0...v2.41.1) diff --git a/pyproject.toml b/pyproject.toml index 75d0d5e246..f3d0b5edf2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.41.1" +version = "2.42.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 55f1df75a9..aeadf9ba57 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.41.1" # x-release-please-version +__version__ = "2.42.0" # x-release-please-version From 4d354538e8c2618e228c80f1fdc332ee1f70d1f2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:43:01 +0000 Subject: [PATCH 394/408] feat(api): update OpenAPI spec or Stainless config --- .stats.yml | 4 ++-- .../realtime/realtime_response_create_mcp_tool.py | 12 +++++++++--- .../realtime_response_create_mcp_tool_param.py | 12 +++++++++--- .../realtime/realtime_session_create_response.py | 12 +++++++++--- .../types/realtime/realtime_tools_config_param.py | 12 +++++++++--- .../types/realtime/realtime_tools_config_union.py | 12 +++++++++--- .../realtime/realtime_tools_config_union_param.py | 12 +++++++++--- src/openai/types/responses/tool.py | 12 +++++++++--- src/openai/types/responses/tool_param.py | 12 +++++++++--- src/openai/types/video_edit_params.py | 2 +- 10 files changed, 75 insertions(+), 27 deletions(-) diff --git a/.stats.yml b/.stats.yml index 693dbe3631..f6b4053c1b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 264 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-4db8edc05bd5503d4aaad74e8b9c783aa1f4d40382a8075c53355bd18dfaf68c.yml -openapi_spec_hash: 2466f6ad496b27334217999202e185c0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-b5b621065906a2579dc180db1236ee3b08a4fca9539accc2fbbf88da0ca3923f.yml +openapi_spec_hash: 45b1b4692b26e714008d8120ccfc7433 config_hash: ef3ce17315a31703e7af0567b3e9738c diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py index cb5eae42d4..bff59b7ae6 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py @@ -118,8 +118,8 @@ class RealtimeResponseCreateMcpTool(BaseModel): ] = None """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -152,5 +152,11 @@ class RealtimeResponseCreateMcpTool(BaseModel): server_url: Optional[str] = None """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py index dd8c2e018f..4cc427cc13 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py @@ -118,8 +118,8 @@ class RealtimeResponseCreateMcpToolParam(TypedDict, total=False): ] """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -152,5 +152,11 @@ class RealtimeResponseCreateMcpToolParam(TypedDict, total=False): server_url: str """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 7193eafcc1..7cd3ce189a 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -340,8 +340,8 @@ class ToolMcpTool(BaseModel): ] = None """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -374,7 +374,13 @@ class ToolMcpTool(BaseModel): server_url: Optional[str] = None """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py index 217130922a..0a9ec2fdd9 100644 --- a/src/openai/types/realtime/realtime_tools_config_param.py +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -121,8 +121,8 @@ class Mcp(TypedDict, total=False): ] """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -155,7 +155,13 @@ class Mcp(TypedDict, total=False): server_url: str """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py index 55da58269c..968a096475 100644 --- a/src/openai/types/realtime/realtime_tools_config_union.py +++ b/src/openai/types/realtime/realtime_tools_config_union.py @@ -121,8 +121,8 @@ class Mcp(BaseModel): ] = None """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -155,7 +155,13 @@ class Mcp(BaseModel): server_url: Optional[str] = None """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py index 15118f3388..f84b6f7ce4 100644 --- a/src/openai/types/realtime/realtime_tools_config_union_param.py +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -120,8 +120,8 @@ class Mcp(TypedDict, total=False): ] """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -154,7 +154,13 @@ class Mcp(TypedDict, total=False): server_url: str """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 92e6fb29a7..33bfcd41ff 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -145,8 +145,8 @@ class Mcp(BaseModel): ] = None """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -179,7 +179,13 @@ class Mcp(BaseModel): server_url: Optional[str] = None """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index 7a9a566b16..a2f7272c36 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -145,8 +145,8 @@ class Mcp(TypedDict, total=False): ] """Identifier for service connectors, like those available in ChatGPT. - One of `server_url` or `connector_id` must be provided. Learn more about service - connectors + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). Currently supported `connector_id` values are: @@ -179,7 +179,13 @@ class Mcp(TypedDict, total=False): server_url: str """The URL for the MCP server. - One of `server_url` or `connector_id` must be provided. + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. """ diff --git a/src/openai/types/video_edit_params.py b/src/openai/types/video_edit_params.py index 8d3b15fc6f..44e8c35d7f 100644 --- a/src/openai/types/video_edit_params.py +++ b/src/openai/types/video_edit_params.py @@ -19,7 +19,7 @@ class VideoEditParams(TypedDict, total=False): class VideoVideoReferenceInputParam(TypedDict, total=False): - """Reference to the completed video.""" + """Reference to the completed video to edit.""" id: Required[str] """The identifier of the completed video.""" From e20b6b82c145091f0d8412a7e4a9bc5900a40462 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 16:43:56 +0000 Subject: [PATCH 395/408] release: 2.43.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c33eb8cbe1..bee3a52fce 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.42.0" + ".": "2.43.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ed57a6ca21..7a467b3b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.43.0 (2026-06-17) + +Full Changelog: [v2.42.0...v2.43.0](https://github.com/openai/openai-python/compare/v2.42.0...v2.43.0) + +### Features + +* **api:** update OpenAPI spec or Stainless config ([2254235](https://github.com/openai/openai-python/commit/22542358490ef8f31f0d373e17f7b791b3d983ca)) + ## 2.42.0 (2026-06-16) Full Changelog: [v2.41.1...v2.42.0](https://github.com/openai/openai-python/compare/v2.41.1...v2.42.0) diff --git a/pyproject.toml b/pyproject.toml index f3d0b5edf2..647b30ac8f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.42.0" +version = "2.43.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index aeadf9ba57..6c046bf3b6 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.42.0" # x-release-please-version +__version__ = "2.43.0" # x-release-please-version From f47f9a270861ca6a6096e8ea15ef8f3e1fb089b0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 19:41:22 +0000 Subject: [PATCH 396/408] fix(auth): prioritize first auth header --- pyproject.toml | 2 +- src/openai/_client.py | 26 +- uv.lock | 2387 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2400 insertions(+), 15 deletions(-) create mode 100644 uv.lock diff --git a/pyproject.toml b/pyproject.toml index 647b30ac8f..239edef8c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.11, <5", "typing-extensions>=4.14, <5", + "typing-extensions>=4.14, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", diff --git a/src/openai/_client.py b/src/openai/_client.py index 499a62dfe5..a3ca9a7148 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -441,15 +441,14 @@ def _send_request( @override def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + headers: dict[str, str] = {} if security.get("bearer_auth", False): - headers = self._bearer_auth - if headers: - return headers - + for key, value in self._bearer_auth.items(): + headers.setdefault(key, value) if security.get("admin_api_key_auth", False): - return self._admin_api_key_auth - - return {} + for key, value in self._admin_api_key_auth.items(): + headers.setdefault(key, value) + return headers @property def _bearer_auth(self) -> dict[str, str]: @@ -947,15 +946,14 @@ async def _send_request( @override def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + headers: dict[str, str] = {} if security.get("bearer_auth", False): - headers = self._bearer_auth - if headers: - return headers - + for key, value in self._bearer_auth.items(): + headers.setdefault(key, value) if security.get("admin_api_key_auth", False): - return self._admin_api_key_auth - - return {} + for key, value in self._admin_api_key_auth.items(): + headers.setdefault(key, value) + return headers @property def _bearer_auth(self) -> dict[str, str]: diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000000..3677540704 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2387 @@ +version = 1 +revision = 3 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "aiohappyeyeballs", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "aiosignal", marker = "python_full_version < '3.10'" }, + { name = "async-timeout", marker = "python_full_version < '3.10'" }, + { name = "attrs", marker = "python_full_version < '3.10'" }, + { name = "frozenlist", marker = "python_full_version < '3.10'" }, + { name = "multidict", marker = "python_full_version < '3.10'" }, + { name = "propcache", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "yarl", version = "1.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/85/cebc47ee74d8b408749073a1a46c6fcba13d170dc8af7e61996c6c9394ac/aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b", size = 750547, upload-time = "2026-03-31T21:56:30.024Z" }, + { url = "https://files.pythonhosted.org/packages/05/98/afd308e35b9d3d8c9ec54c0918f1d722c86dc17ddfec272fcdbcce5a3124/aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5", size = 503535, upload-time = "2026-03-31T21:56:31.935Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4d/926c183e06b09d5270a309eb50fbde7b09782bfd305dec1e800f329834fb/aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670", size = 497830, upload-time = "2026-03-31T21:56:33.654Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d6/f47d1c690f115a5c2a5e8938cce4a232a5be9aac5c5fb2647efcbbbda333/aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274", size = 1682474, upload-time = "2026-03-31T21:56:35.513Z" }, + { url = "https://files.pythonhosted.org/packages/01/44/056fd37b1bb52eac760303e5196acc74d9d546631b035704ae5927f7b4ac/aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a", size = 1655259, upload-time = "2026-03-31T21:56:37.843Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/78eb1a20c1c28ae02f6a3c0f4d7b0dcc66abce5290cadd53d78ce3084175/aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d", size = 1736204, upload-time = "2026-03-31T21:56:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/de/6c/d20d7de23f0b52b8c1d9e2033b2db1ac4dacbb470bb74c56de0f5f86bb4f/aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796", size = 1826198, upload-time = "2026-03-31T21:56:41.378Z" }, + { url = "https://files.pythonhosted.org/packages/2f/86/a6f3ff1fd795f49545a7c74b2c92f62729135d73e7e4055bf74da5a26c82/aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95", size = 1681329, upload-time = "2026-03-31T21:56:43.374Z" }, + { url = "https://files.pythonhosted.org/packages/fb/68/84cd3dab6b7b4f3e6fe9459a961acb142aaab846417f6e8905110d7027e5/aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5", size = 1560023, upload-time = "2026-03-31T21:56:45.031Z" }, + { url = "https://files.pythonhosted.org/packages/41/2c/db61b64b0249e30f954a65ab4cb4970ced57544b1de2e3c98ee5dc24165f/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a", size = 1652372, upload-time = "2026-03-31T21:56:47.075Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/e96988a6c982d047810c772e28c43c64c300c943b0ed5c1c0c4ce1e1027c/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73", size = 1662031, upload-time = "2026-03-31T21:56:48.835Z" }, + { url = "https://files.pythonhosted.org/packages/b7/26/a56feace81f3d347b4052403a9d03754a0ab23f7940780dada0849a38c92/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297", size = 1708118, upload-time = "2026-03-31T21:56:50.833Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/b6173a8ff03d01d5e1a694bc06764b5dad1df2d4ed8f0ceec12bb3277936/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074", size = 1548667, upload-time = "2026-03-31T21:56:52.81Z" }, + { url = "https://files.pythonhosted.org/packages/16/13/13296ffe2c132d888b3fe2c195c8b9c0c24c89c3fa5cc2c44464dc23b22e/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e", size = 1724490, upload-time = "2026-03-31T21:56:54.541Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1f1c287f4a79782ef36e5a6e62954c85343bc30470d862d30bd5f26c9fa2/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7", size = 1667109, upload-time = "2026-03-31T21:56:56.21Z" }, + { url = "https://files.pythonhosted.org/packages/ef/42/8461a2aaf60a8f4ea4549a4056be36b904b0eb03d97ca9a8a2604681a500/aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9", size = 439478, upload-time = "2026-03-31T21:56:58.292Z" }, + { url = "https://files.pythonhosted.org/packages/e5/71/06956304cb5ee439dfe8d86e1b2e70088bd88ed1ced1f42fb29e5d855f0e/aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76", size = 462047, upload-time = "2026-03-31T21:57:00.257Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a5/630bc484695d4a1342bbae85fb8689bf979106525684fc88f05b397324ad/aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf", size = 752872, upload-time = "2026-03-31T22:00:15.553Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b8/6a19dda37fda94a9ebefb3c1ae0ff419ac7fbf4fb40750e992829fc13614/aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1", size = 504582, upload-time = "2026-03-31T22:00:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/d5/34/8413eafee3421ade2d6ce9e7c0da1213e1d7f0049be09dcdc342b03a39ba/aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10", size = 499094, upload-time = "2026-03-31T22:00:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/da/cf/c6f97006093d1e8ca40fbab843ff49ec7725ab668f0714dd1cb702c62cbd/aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f", size = 1669505, upload-time = "2026-03-31T22:00:24.01Z" }, + { url = "https://files.pythonhosted.org/packages/c2/27/3b2288e66dcec8b04771b2bee3909f70e4072bea995cde5ab7e775e73ddc/aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b", size = 1648928, upload-time = "2026-03-31T22:00:27.001Z" }, + { url = "https://files.pythonhosted.org/packages/3a/7f/605d766887594a88dcc27a19663499c7c5e13e7aa87f129b763765a2ee63/aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643", size = 1731800, upload-time = "2026-03-31T22:00:29.603Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/5a878e728e30699d22b118f1a6ad576ab6fff9eb2c6fc8a7faa9376a1c3e/aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031", size = 1824247, upload-time = "2026-03-31T22:00:32.139Z" }, + { url = "https://files.pythonhosted.org/packages/37/99/84b448291e9996bb83bf4fad3a71a9786d542f19c50a3ff0531bfaba6fac/aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258", size = 1670742, upload-time = "2026-03-31T22:00:34.788Z" }, + { url = "https://files.pythonhosted.org/packages/14/a8/d8d5d1ab6d29a4a3bdb9db31f161e338bfdf6638f6574ea8380f1d4a243c/aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a", size = 1562474, upload-time = "2026-03-31T22:00:37.623Z" }, + { url = "https://files.pythonhosted.org/packages/92/e8/bd889697916f10b65524422c61b4eeaf919eb35a170290cccb680cbe4eb4/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88", size = 1642235, upload-time = "2026-03-31T22:00:40.541Z" }, + { url = "https://files.pythonhosted.org/packages/60/42/3f1928107131f1413a5972ace14ddcd5364968e9bd7b3ad71272defafc9c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0", size = 1655397, upload-time = "2026-03-31T22:00:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/b2/79/c4bbcf4cac3a4715a326e49720ccdc3a4b5e14a367c5029eae7727d06029/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f", size = 1703509, upload-time = "2026-03-31T22:00:45.908Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e6/32d245876f211a7308a7d5437707f9296b1f9837a2888a407ed04e61321c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8", size = 1550098, upload-time = "2026-03-31T22:00:49.48Z" }, + { url = "https://files.pythonhosted.org/packages/db/62/ab0f1304def56ce2356e6fbb9f0b024d6544010351430070f48f53b89e0a/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f", size = 1724326, upload-time = "2026-03-31T22:00:52.165Z" }, + { url = "https://files.pythonhosted.org/packages/c4/9a/aab4469689024046220ea438aa020ea2ae04cd1dd71aea3057e094f8c357/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b", size = 1658824, upload-time = "2026-03-31T22:00:55.122Z" }, + { url = "https://files.pythonhosted.org/packages/b0/98/bcc35d4db687acabf06d41f561a99fa88bca145292513388c858d99b72c5/aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83", size = 440302, upload-time = "2026-03-31T22:00:57.673Z" }, + { url = "https://files.pythonhosted.org/packages/25/61/b0203c2ef6bd268fca0eda142f0efbba7cbebd7ad38f7bb01dd31c2ff68e/aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67", size = 463076, upload-time = "2026-03-31T22:01:00.264Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "aiohappyeyeballs", version = "2.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "aiosignal", marker = "python_full_version >= '3.10'" }, + { name = "async-timeout", marker = "python_full_version == '3.10.*'" }, + { name = "attrs", marker = "python_full_version >= '3.10'" }, + { name = "frozenlist", marker = "python_full_version >= '3.10'" }, + { name = "multidict", marker = "python_full_version >= '3.10'" }, + { name = "propcache", version = "0.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, + { name = "yarl", version = "1.24.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/67/58ded4b3f2e10f94972d8928050c85330e249a31dd45a0e5f3c0e9c3fa05/aiohttp-3.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e", size = 766140, upload-time = "2026-06-07T21:05:37.471Z" }, + { url = "https://files.pythonhosted.org/packages/18/68/4ae5b4e08943f316594bb68da89957d3baf5760588fa09509594bd777e4b/aiohttp-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491", size = 519430, upload-time = "2026-06-07T21:05:40.751Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c1/316c8f3549dbe5245f92bfd523ec6f32dd4d98cafe21df3f6a19b1184c75/aiohttp-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce", size = 514406, upload-time = "2026-06-07T21:05:42.111Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ee/fb0ac28684e8d753b83c8a4eebc19a5846912aa0a4daaabb6a9936363840/aiohttp-3.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3", size = 1703649, upload-time = "2026-06-07T21:05:43.427Z" }, + { url = "https://files.pythonhosted.org/packages/3b/57/aa2beab673331f111885db8a7b69dfe3ab0e53e446a0ace18ca694b4dc58/aiohttp-3.14.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505", size = 1675126, upload-time = "2026-06-07T21:05:44.897Z" }, + { url = "https://files.pythonhosted.org/packages/47/ea/dad128abe365e79be03b16ed464198ac73e0d257e8260c6f7d6f31cbef26/aiohttp-3.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521", size = 1771558, upload-time = "2026-06-07T21:05:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/63/f3/b5b4e10327cb85d34d24232c6b71b64602f190b3ccb238a043ac6b187dac/aiohttp-3.14.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd", size = 1856631, upload-time = "2026-06-07T21:05:47.844Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/93294c3045775c708ac8310eb3d3622a11d2951345ad590d532d62a1faa4/aiohttp-3.14.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb", size = 1714139, upload-time = "2026-06-07T21:05:49.982Z" }, + { url = "https://files.pythonhosted.org/packages/29/c4/93067c85a0373492ce8e577435203c5947c454af074ac48ed4f3a1b9dd4a/aiohttp-3.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42", size = 1588321, upload-time = "2026-06-07T21:05:51.431Z" }, + { url = "https://files.pythonhosted.org/packages/c4/39/9ff91aaf02af8b7b8222a987466da539f154c3e01732c22b5f5a20a8ee66/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b", size = 1670375, upload-time = "2026-06-07T21:05:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e4/77452a3676b8d99ac1375f77691d6bf65ea6e9f4b201b82ef77c916dc767/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192", size = 1690933, upload-time = "2026-06-07T21:05:54.902Z" }, + { url = "https://files.pythonhosted.org/packages/7d/84/b0059a7c7fc05ea23f3bc1596ba91c12f79588b9450564a24cac37536d0a/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05", size = 1740798, upload-time = "2026-06-07T21:05:56.458Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3a/e2a513ecbfc362591caa51a7f7e011b3bfc8938b388ae44cd95560d36999/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe", size = 1576412, upload-time = "2026-06-07T21:05:57.953Z" }, + { url = "https://files.pythonhosted.org/packages/a1/10/08f1654f538f93d36dcac66310a06eefce4641cdafca83f9f0a5317be254/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d", size = 1750199, upload-time = "2026-06-07T21:05:59.488Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/d91b70c57d8b8e9611e4a2e52238ca3698d3dc1c2efe25b7a9bf594ac584/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966", size = 1699356, upload-time = "2026-06-07T21:06:01.131Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f1/15340176f35ff61b95dbe34020bcf43f9e624a2d7bbac934715ff97d2033/aiohttp-3.14.1-cp310-cp310-win32.whl", hash = "sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6", size = 458939, upload-time = "2026-06-07T21:06:02.86Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/a2f1ec5b37f903109e43ae2862268cfe4a67a60c1b2cf43169fcdff5995f/aiohttp-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df", size = 482583, upload-time = "2026-06-07T21:06:04.666Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7a/7b56f6732ef79530afaa72aa335d41b67c8d79b946995f0b11ad72985435/aiohttp-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c", size = 453470, upload-time = "2026-06-07T21:06:06.322Z" }, + { url = "https://files.pythonhosted.org/packages/26/dd/bf526e6f0a1120dd6f2df2e97bacfe4d358f13d17a0ff5847301a1375a51/aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2", size = 765225, upload-time = "2026-06-07T21:06:07.957Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e1/a2872aa55495a70f61310d411541c6ee23812d9a884e000c716e1bc3edbf/aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f", size = 518743, upload-time = "2026-06-07T21:06:09.749Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e7/c60c7b209e509cc787de3cea0550a518538cfc08003e1c1e14c1c63fff71/aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8", size = 514139, upload-time = "2026-06-07T21:06:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8d/614ace2f579702c9840ab1e1447fd8509e35b0b904f7196418fa2f57b25d/aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04", size = 1784088, upload-time = "2026-06-07T21:06:12.887Z" }, + { url = "https://files.pythonhosted.org/packages/49/e0/726e90f99542bf292f81a96a12cc4847deb86f3ccf62c6f4014a201f4d33/aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8", size = 1737835, upload-time = "2026-06-07T21:06:14.564Z" }, + { url = "https://files.pythonhosted.org/packages/0b/4b/d176d5c4db9d33dacf0543102ea59503bc1d528af4cfd0b719949ca49389/aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6", size = 1842801, upload-time = "2026-06-07T21:06:16.228Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d6/5a99b563690ea0cbed912ae94a2ce33993a5709a651a3a4fe761e7dd973a/aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af", size = 1929992, upload-time = "2026-06-07T21:06:17.947Z" }, + { url = "https://files.pythonhosted.org/packages/76/7f/a987b14a3859094b3cea3f4825219c3e5536242564af6e3f9c2f6c994eb2/aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730", size = 1786989, upload-time = "2026-06-07T21:06:19.677Z" }, + { url = "https://files.pythonhosted.org/packages/f1/1a/420e5c85a3e73349372ed22ce0b6af86bfa6ce16a4b20a64a2e94608c781/aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621", size = 1640129, upload-time = "2026-06-07T21:06:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/a7/80/18a592ed3be0a402cc03670bd72ee1f8563ddbe1d8d5542dbf868f274136/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee", size = 1756576, upload-time = "2026-06-07T21:06:24.8Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0b/8b3d5713373858ff71a617daf6e3b0e81ad63e79d09a3cf2f6b6b983939c/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573", size = 1754668, upload-time = "2026-06-07T21:06:26.528Z" }, + { url = "https://files.pythonhosted.org/packages/9f/49/fd564575cf225821d7ba5a117cb8bc27213d8a7e1811162afb43ae077039/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7", size = 1817019, upload-time = "2026-06-07T21:06:28.297Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/e850c9ae6fc91356552ae668bb6c51e93fa29c8aef13398a10b56678557f/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf", size = 1631638, upload-time = "2026-06-07T21:06:30.242Z" }, + { url = "https://files.pythonhosted.org/packages/eb/94/3c337ba72451a89806ace6f75bddc92bafc5b8d53d90115a512858024b63/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85", size = 1835660, upload-time = "2026-06-07T21:06:31.943Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9c/9c18cf367a0498212d9ba7daf990b504a5e8ae064cda4b504e2647c89c03/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3", size = 1775698, upload-time = "2026-06-07T21:06:33.72Z" }, + { url = "https://files.pythonhosted.org/packages/b5/63/a251a9d2a6cb45065b2ddc0bde2b3dd10108740a9a42f632c66405a761a2/aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126", size = 458386, upload-time = "2026-06-07T21:06:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/17/ca/69274c51dcd6e8947d77b2806cf47a4a15f2c846e2cbeb1882547d3da283/aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5", size = 483406, upload-time = "2026-06-07T21:06:36.824Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8a/c25904f77690c3688ec140f87591ef11a0cfe36bf3d5c0f1f38056fb62b3/aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b", size = 452987, upload-time = "2026-06-07T21:06:38.371Z" }, + { url = "https://files.pythonhosted.org/packages/1d/21/151624b51cd92553d95424daf4bf19f19ce9be9002d19253e7e7ce67197b/aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480", size = 757402, upload-time = "2026-06-07T21:06:40.311Z" }, + { url = "https://files.pythonhosted.org/packages/c2/82/280619e0bd7bf2454987e19282616e84762255dd9c8468f62382e8c191f1/aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d", size = 512310, upload-time = "2026-06-07T21:06:42.207Z" }, + { url = "https://files.pythonhosted.org/packages/55/b2/2aac325583aaa1353045f96dffa586d8a34e8322e14a7ba49cffeb103ab4/aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2", size = 512448, upload-time = "2026-06-07T21:06:43.813Z" }, + { url = "https://files.pythonhosted.org/packages/8a/72/a60607cb849faa8af8a356c9329ea2eb6f395d49e82cc82ccba1fd8deb8f/aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2", size = 1766854, upload-time = "2026-06-07T21:06:45.391Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d3/d9fe1c9ec7557ab4d0d82bebaa728c6418f0b93295ec2f4ab015f7710cc7/aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a", size = 1740884, upload-time = "2026-06-07T21:06:47.413Z" }, + { url = "https://files.pythonhosted.org/packages/c1/dc/f2cecfaf9337ba3e63f181500814ff502aa3d00d9c7ec93a9d23d10a27b2/aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264", size = 1810034, upload-time = "2026-06-07T21:06:50.165Z" }, + { url = "https://files.pythonhosted.org/packages/66/d7/2ff65c5e65c0d7476daf7e15c032e0805e36811185b9623e3238ad6c763e/aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842", size = 1904054, upload-time = "2026-06-07T21:06:52.035Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/d445818389df371f56d141d881153ba23183c4735a03f7356ffb43f7757d/aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c", size = 1790278, upload-time = "2026-06-07T21:06:54.049Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/bf04cb4d865fc6101c2229a294ad744973b72e513fdc5a6b791e6983d72a/aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95", size = 1591795, upload-time = "2026-06-07T21:06:55.911Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b4/4dac0038960427ba832f6609dfb4ea5437d7fd80c72001b9e48f834f428b/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199", size = 1728397, upload-time = "2026-06-07T21:06:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/7cd4e8ad7aa3b75f17d56bb5498dd604a93d4e6eece822ba0568c413fff0/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817", size = 1766504, upload-time = "2026-06-07T21:07:00.009Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/fc01d9fcad0f73fed3f3d361f1f94f975947b50dff82919f6dc2bf4316cc/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a", size = 1777806, upload-time = "2026-06-07T21:07:02.064Z" }, + { url = "https://files.pythonhosted.org/packages/41/09/47e2d090bddcc8fb4ccb4c314aadc32d7c5d9bb55f50f6ad1c92fc15d501/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4", size = 1580707, upload-time = "2026-06-07T21:07:03.942Z" }, + { url = "https://files.pythonhosted.org/packages/3d/36/f1a4ce904ae0b6930cfe9afc96d0896f7ec1a620c400405d63783bb95a9c/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087", size = 1798121, upload-time = "2026-06-07T21:07:05.987Z" }, + { url = "https://files.pythonhosted.org/packages/70/0a/e0075ce9ca0279ee1d4f0c0b85f54fea02ebc83c3007651a72bece658fec/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3", size = 1767580, upload-time = "2026-06-07T21:07:07.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/a0c0a8f327a9c52095cdd8e312391b00d3ed64ab6c72bb5c33d8ec251cf7/aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4", size = 452771, upload-time = "2026-06-07T21:07:09.669Z" }, + { url = "https://files.pythonhosted.org/packages/df/d9/ea367c75f16ac9c6cdc8febb25e8318fa21a2b1bc8d6514d4b2d890bface/aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271", size = 479873, upload-time = "2026-06-07T21:07:11.538Z" }, + { url = "https://files.pythonhosted.org/packages/03/64/8d96784a7851156db8a4c6c3f6f91042fdf39fb15a4cc38c8b3c14833c45/aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847", size = 448073, upload-time = "2026-06-07T21:07:13.637Z" }, + { url = "https://files.pythonhosted.org/packages/bc/97/bd137012dd97e1649162b099135a80e1fd59aaa807b2430fc448d1029aff/aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178", size = 506882, upload-time = "2026-06-07T21:07:15.501Z" }, + { url = "https://files.pythonhosted.org/packages/ef/79/e5cc690e9d922a66887ceeaca53a8ffd5a7b0be3816142b7abc433742d89/aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf", size = 515270, upload-time = "2026-06-07T21:07:17.53Z" }, + { url = "https://files.pythonhosted.org/packages/fe/22/a73ccbf9dbd6e26dda0b24d5fd5db7da92ee3383a79f47677ffb834c5c5b/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd", size = 485841, upload-time = "2026-06-07T21:07:19.555Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b9/57ed8eaf596321c2ad747bd480fb1700dbd7177c60dfc9e4c187f629662e/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe", size = 492088, upload-time = "2026-06-07T21:07:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/78/c0/5ebe5270a7c140d7c6f79dcb018640225f14d406c149e4eec04a7d82fe71/aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4", size = 501564, upload-time = "2026-06-07T21:07:23.388Z" }, + { url = "https://files.pythonhosted.org/packages/75/7f/8cdaa24fc7983865e0915153b96a9ac5bcdd3548d64c5a27d17cecccad2d/aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876", size = 751998, upload-time = "2026-06-07T21:07:25.046Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f4/c4227aacfacc5cb0cc2d119b65301d177912a6842cd64e120c47af76064f/aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da", size = 510918, upload-time = "2026-06-07T21:07:27.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/01/a2d5f96cd4e74424864d30bc0a7e44d0a12dacdcfa91b5b2d1bd3dca6bf3/aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6", size = 508657, upload-time = "2026-06-07T21:07:29.252Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ed/3c0fb5c500fdd8e7ebc10d1889c04384fffa1a9163eac1356088ca9da1b1/aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96", size = 1757907, upload-time = "2026-06-07T21:07:31.03Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ab/d4c924d9bd5be3050c226612413ce68cb54c70d2c31b661bfc8d9a5b6a70/aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f", size = 1737565, upload-time = "2026-06-07T21:07:33.031Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/37326821ff779084020cdc33224d20b19f42f4183a500ff92022a739eda7/aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296", size = 1799018, upload-time = "2026-06-07T21:07:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/b3/4f/6e947ba73e4ce09070761c05ed3a8ceb7c21f5e46798671d8b2aac0e4626/aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa", size = 1894416, upload-time = "2026-06-07T21:07:36.956Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6e/dbf1d0625dc711fb2851f4f3c3055c39ed58bae92082d8c627dbe6013736/aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451", size = 1783881, upload-time = "2026-06-07T21:07:39.063Z" }, + { url = "https://files.pythonhosted.org/packages/44/c2/5e25098a67268ed369483ae7d1a58bd0a13d03aab860d2a0e4a6eb25b046/aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c", size = 1587572, upload-time = "2026-06-07T21:07:41.058Z" }, + { url = "https://files.pythonhosted.org/packages/2a/bd/cf9cee17e140f942a3de73e658a543aa8fbf35a5fc67a9d2538d52d77f0b/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca", size = 1722137, upload-time = "2026-06-07T21:07:43.014Z" }, + { url = "https://files.pythonhosted.org/packages/89/6d/5684f8c59045c96f81a18cefbc1fbbd79d25b88f1c622f2a5c5c08fcb632/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09", size = 1755953, upload-time = "2026-06-07T21:07:45.933Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/35caf3170f8359760740a7d9aa0fff2e344bef98e1d1186f5a0f6dec17e6/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397", size = 1766479, upload-time = "2026-06-07T21:07:48.047Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a1/b0c61e7a137f0d81de49a82023a6df73c3c16d6fefb0f8e4a93d21639002/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080", size = 1580077, upload-time = "2026-06-07T21:07:50.069Z" }, + { url = "https://files.pythonhosted.org/packages/0b/41/194ea4623693009fcefebef7aef63c141754f153e9cd0d39d3b9e36c175c/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345", size = 1791688, upload-time = "2026-06-07T21:07:52.106Z" }, + { url = "https://files.pythonhosted.org/packages/ba/45/4de841f005cfe1fd63e2a2fe011262c515e2a62aa6994b15947e7d717ac9/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588", size = 1761094, upload-time = "2026-06-07T21:07:54.113Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ae/dbce10533d3896d544d5053939ed75b7dc31a1b0973d959b1b5ae21028d6/aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780", size = 452662, upload-time = "2026-06-07T21:07:56.06Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/0bf1a19362c32f06229da5e7ddfcec91f93474d6307f7a2d3135e9c674dc/aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a", size = 479748, upload-time = "2026-06-07T21:07:58.319Z" }, + { url = "https://files.pythonhosted.org/packages/22/0a/62e7232dc9484fbec112ceb32efb6a624cc7994ec6e2b019286f17c4e8f2/aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8", size = 447723, upload-time = "2026-06-07T21:08:00.154Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a1/5fafa04e1ca91ddb47608699d60649c1c6db3cf41c99e78fc4056f9513db/aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15", size = 508531, upload-time = "2026-06-07T21:08:02.093Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/bfa02f699d87ffc86d5959270b28f1cb410add3ccaced8ed2e0b8a5238fc/aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8", size = 514718, upload-time = "2026-06-07T21:08:04.476Z" }, + { url = "https://files.pythonhosted.org/packages/85/a5/9594ad6289eebbc97d167c44213d557807f90e59115caad24de21ad2c3b1/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3", size = 487918, upload-time = "2026-06-07T21:08:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/b4/61/16a32c36c3c49edec122a3dc811f2057df2f94d3b14aa107c8017d981618/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba", size = 494014, upload-time = "2026-06-07T21:08:08.263Z" }, + { url = "https://files.pythonhosted.org/packages/9b/89/3ebcf96ed99c05bec9c434aaac6963fd3cbab4a786ae739908a144d9ce44/aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397", size = 502398, upload-time = "2026-06-07T21:08:10.244Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3d/b74870a0c2d40c355928cd5b96c7a11fa821b8a40fc41365e64479b151fb/aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448", size = 758018, upload-time = "2026-06-07T21:08:12.447Z" }, + { url = "https://files.pythonhosted.org/packages/d3/66/f42f5c984d99e49c6cff5f26f590750f2e2f7ef1fcfb99966ab5be1b632e/aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004", size = 512462, upload-time = "2026-06-07T21:08:14.624Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a7/248e1aebe0c7810b0271e021a0f2a5eb6e78a051885b3c9df49f42a5802d/aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983", size = 512824, upload-time = "2026-06-07T21:08:16.572Z" }, + { url = "https://files.pythonhosted.org/packages/26/97/2aa0e5ba0727dc3bd5aaebb7ccbc510f7dfb7fb961ec87497cd496635ab1/aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe", size = 1749898, upload-time = "2026-06-07T21:08:18.635Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/e97f6c96c891d457c8479d92a514ba194d0412f981d72c70341ee18488ed/aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333", size = 1710114, upload-time = "2026-06-07T21:08:20.892Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e6/aa8d7e863048c8fceb5cd6ce74017311cec3ead07847387e12265fb4444e/aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0", size = 1802541, upload-time = "2026-06-07T21:08:23.044Z" }, + { url = "https://files.pythonhosted.org/packages/83/a8/72193137de57fda4ebfae4563182d082c8856e3b6e9871d0b46f028fb369/aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a", size = 1875776, upload-time = "2026-06-07T21:08:25.288Z" }, + { url = "https://files.pythonhosted.org/packages/a0/18/938441025db6769a3464596b2410af3afde0b21eb2f204c6f766f68af4bd/aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602", size = 1760329, upload-time = "2026-06-07T21:08:27.363Z" }, + { url = "https://files.pythonhosted.org/packages/60/29/bf2496b4065e76e09fe48015aaffe5ce161d8f089b06ac6982070f653076/aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca", size = 1587293, upload-time = "2026-06-07T21:08:29.805Z" }, + { url = "https://files.pythonhosted.org/packages/49/a2/2136674d52123b1354bd05dd5753c318db47dc0c927cc70b27bab3755456/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35", size = 1714756, upload-time = "2026-06-07T21:08:32.094Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/e5fd2e6f915503081c0f9b1e8540947037929c70c191da2e4d54b31a21a1/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844", size = 1721052, upload-time = "2026-06-07T21:08:34.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/5a/2833e324a2263e104e31e2e91bc5bbee81bc499afd32203faee048a883f0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496", size = 1766888, upload-time = "2026-06-07T21:08:36.95Z" }, + { url = "https://files.pythonhosted.org/packages/57/fa/dea6511870913162f3b2e8c42a7614eb203a4540b8c2da43e0bfb0548f3c/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5", size = 1581679, upload-time = "2026-06-07T21:08:39.292Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/3cf0d55e71784b33534e9710a67d382d900598b4787fbce6cc7317f8c42a/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95", size = 1782021, upload-time = "2026-06-07T21:08:41.407Z" }, + { url = "https://files.pythonhosted.org/packages/c1/af/14bb5843eccbe234f4dfb78ab73e549d99727247e62ae5d62cbd22eaf5b0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444", size = 1742574, upload-time = "2026-06-07T21:08:43.795Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1e/fbeb7af9210a67ac0f9c9bec0f8f4568497924e33137a3d5b48e1cf85f3f/aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0", size = 457773, upload-time = "2026-06-07T21:08:46.168Z" }, + { url = "https://files.pythonhosted.org/packages/f0/2b/13e8d741a9ec5db7d900c060554cf8352ab85e44e2a4469ebb9d377bda17/aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719", size = 485001, upload-time = "2026-06-07T21:08:48.401Z" }, + { url = "https://files.pythonhosted.org/packages/df/30/491acfa2c4d6c3ff59c49a14fc1b50be3241e25bbb0c84c09e2da4d11395/aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec", size = 453809, upload-time = "2026-06-07T21:08:50.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/e3/19dbe1a1f4cc6230eb9e314de7fe68053b0992f9302b27d12141a0b5db53/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2", size = 793320, upload-time = "2026-06-07T21:08:52.775Z" }, + { url = "https://files.pythonhosted.org/packages/7f/20/1b7182219ba1b108430d6e4dc53d25ae02dcfcf5a045b33af4e8c5167527/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340", size = 529077, upload-time = "2026-06-07T21:08:55Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c8/14ce60ec31a2e5f5274bb17d383a6f7a3aabca31ac04eee05585bbadab16/aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d", size = 532476, upload-time = "2026-06-07T21:08:57.176Z" }, + { url = "https://files.pythonhosted.org/packages/7e/02/9ac85e081e53da2e061b02fa7758fe0a12d17b8ce2d1f5e6c7cb76730328/aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94", size = 1922347, upload-time = "2026-06-07T21:08:59.563Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3e/d3ba07a0ab38b5389e10bec4362d21e10a4f667cba2d79ba30837b3a5059/aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc", size = 1786465, upload-time = "2026-06-07T21:09:01.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cb/e2ee978a00cfb2df829704a69528b18154eba5939f45bc1efa8f33aee4c5/aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251", size = 1909423, upload-time = "2026-06-07T21:09:04.357Z" }, + { url = "https://files.pythonhosted.org/packages/73/5d/1430334858b1022b58ae50399a918f0bd6fe8fa7fa183598d657ff61e040/aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1", size = 2001906, upload-time = "2026-06-07T21:09:06.722Z" }, + { url = "https://files.pythonhosted.org/packages/66/4e/560c7472d3d198a23aa5c8b19a5115bf6a9b77b7d3e4bb363da320430ad2/aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3", size = 1877095, upload-time = "2026-06-07T21:09:09.011Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f1/4745806578d447db4a784a8591e2dae3afdfc2bcb96f8f81271b13df6543/aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c", size = 1676222, upload-time = "2026-06-07T21:09:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/48255813cca749a229ef0ab476004ec623728ad79a9c0840616f6c076325/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203", size = 1842922, upload-time = "2026-06-07T21:09:14.118Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c0/bbd054e2bee909f529523a5af3891052606af5143c09f5f183ec3b234676/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1", size = 1825035, upload-time = "2026-06-07T21:09:16.447Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ae/90395d4376deceb74e09ec26b6adf7d2015a6f8802d6d84446af860fef04/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665", size = 1849512, upload-time = "2026-06-07T21:09:18.742Z" }, + { url = "https://files.pythonhosted.org/packages/93/bd/fb25f3049957553d4ce0ba6ae480aa2f592a6985497fca590837d16c1be0/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1", size = 1668571, upload-time = "2026-06-07T21:09:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/3f/22/7f73303d64dd567ff3addca90b556690ed1233a47b8f55d242fb90af3681/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d", size = 1881159, upload-time = "2026-06-07T21:09:23.813Z" }, + { url = "https://files.pythonhosted.org/packages/44/be/0474c5a8b5640e1e4aa1923430a91f4151be82e511373fe764189b89aef5/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c", size = 1841409, upload-time = "2026-06-07T21:09:26.207Z" }, + { url = "https://files.pythonhosted.org/packages/7b/3c/bb4a7cba26956cb3da4553cc2056cf67be5b5ff6e6d8fa4fbdff73bfb7ae/aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365", size = 494166, upload-time = "2026-06-07T21:09:28.505Z" }, + { url = "https://files.pythonhosted.org/packages/8a/84/ec80c2c1f66a952555a9f86df6b33af65108a6febfa0471b69013a12f807/aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9", size = 530255, upload-time = "2026-06-07T21:09:30.843Z" }, + { url = "https://files.pythonhosted.org/packages/2a/71/6e22be134a4061ada85a92951b842f2657f17d926b727f3f94c56ae963d6/aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6", size = 469640, upload-time = "2026-06-07T21:09:33.028Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.12.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, + { name = "idna", marker = "python_full_version < '3.10'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, +] + +[[package]] +name = "anyio" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "certifi" +version = "2026.6.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", version = "2.23", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and implementation_name != 'PyPy'" }, + { name = "pycparser", version = "3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", size = 184288, upload-time = "2025-09-08T23:23:48.404Z" }, + { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", size = 180509, upload-time = "2025-09-08T23:23:49.73Z" }, + { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" }, + { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", size = 216498, upload-time = "2025-09-08T23:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", size = 203243, upload-time = "2025-09-08T23:23:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", size = 203158, upload-time = "2025-09-08T23:23:55.169Z" }, + { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", size = 216548, upload-time = "2025-09-08T23:23:56.506Z" }, + { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", size = 218897, upload-time = "2025-09-08T23:23:57.825Z" }, + { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", size = 211249, upload-time = "2025-09-08T23:23:59.139Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", size = 218041, upload-time = "2025-09-08T23:24:00.496Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", size = 172138, upload-time = "2025-09-08T23:24:01.7Z" }, + { url = "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", size = 182794, upload-time = "2025-09-08T23:24:02.943Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/c2/59/ae5cdac87a00962122ea37bb346d41b66aec05f9ce328fa2b9e216f8967b/frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", size = 86967, upload-time = "2025-10-06T05:37:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/8a/10/17059b2db5a032fd9323c41c39e9d1f5f9d0c8f04d1e4e3e788573086e61/frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", size = 49984, upload-time = "2025-10-06T05:37:57.049Z" }, + { url = "https://files.pythonhosted.org/packages/4b/de/ad9d82ca8e5fa8f0c636e64606553c79e2b859ad253030b62a21fe9986f5/frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", size = 50240, upload-time = "2025-10-06T05:37:58.145Z" }, + { url = "https://files.pythonhosted.org/packages/4e/45/3dfb7767c2a67d123650122b62ce13c731b6c745bc14424eea67678b508c/frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", size = 219472, upload-time = "2025-10-06T05:37:59.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/bf/5bf23d913a741b960d5c1dac7c1985d8a2a1d015772b2d18ea168b08e7ff/frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", size = 221531, upload-time = "2025-10-06T05:38:00.521Z" }, + { url = "https://files.pythonhosted.org/packages/d0/03/27ec393f3b55860859f4b74cdc8c2a4af3dbf3533305e8eacf48a4fd9a54/frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", size = 219211, upload-time = "2025-10-06T05:38:01.842Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ad/0fd00c404fa73fe9b169429e9a972d5ed807973c40ab6b3cf9365a33d360/frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", size = 231775, upload-time = "2025-10-06T05:38:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/c3/86962566154cb4d2995358bc8331bfc4ea19d07db1a96f64935a1607f2b6/frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", size = 236631, upload-time = "2025-10-06T05:38:04.609Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/6ffad161dbd83782d2c66dc4d378a9103b31770cb1e67febf43aea42d202/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", size = 218632, upload-time = "2025-10-06T05:38:05.917Z" }, + { url = "https://files.pythonhosted.org/packages/58/b2/4677eee46e0a97f9b30735e6ad0bf6aba3e497986066eb68807ac85cf60f/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", size = 235967, upload-time = "2025-10-06T05:38:07.614Z" }, + { url = "https://files.pythonhosted.org/packages/05/f3/86e75f8639c5a93745ca7addbbc9de6af56aebb930d233512b17e46f6493/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", size = 228799, upload-time = "2025-10-06T05:38:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/30/00/39aad3a7f0d98f5eb1d99a3c311215674ed87061aecee7851974b335c050/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", size = 230566, upload-time = "2025-10-06T05:38:10.52Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4d/aa144cac44568d137846ddc4d5210fb5d9719eb1d7ec6fa2728a54b5b94a/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", size = 217715, upload-time = "2025-10-06T05:38:11.832Z" }, + { url = "https://files.pythonhosted.org/packages/64/4c/8f665921667509d25a0dd72540513bc86b356c95541686f6442a3283019f/frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", size = 39933, upload-time = "2025-10-06T05:38:13.061Z" }, + { url = "https://files.pythonhosted.org/packages/79/bd/bcc926f87027fad5e59926ff12d136e1082a115025d33c032d1cd69ab377/frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", size = 44121, upload-time = "2025-10-06T05:38:14.572Z" }, + { url = "https://files.pythonhosted.org/packages/4c/07/9c2e4eb7584af4b705237b971b89a4155a8e57599c4483a131a39256a9a0/frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", size = 40312, upload-time = "2025-10-06T05:38:15.699Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "anyio", version = "4.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-aiohttp" +version = "0.1.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "aiohttp", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/2c/b894861cecf030fb45675ea24aa55b5722e97c602a163d872fca66c5a6d8/httpx_aiohttp-0.1.12.tar.gz", hash = "sha256:81feec51fd82c0ecfa0e9aaf1b1a6c2591260d5e2bcbeb7eb0277a78e610df2c", size = 275945, upload-time = "2025-12-12T10:12:15.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/8d/85c9701e9af72ca132a1783e2a54364a90c6da832304416a30fc11196ab2/httpx_aiohttp-0.1.12-py3-none-any.whl", hash = "sha256:5b0eac39a7f360fa7867a60bcb46bb1024eada9c01cbfecdb54dc1edb3fb7141", size = 6367, upload-time = "2025-12-12T10:12:14.018Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "jiter" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/da/76a2c7e510ba15fe323d9509c223ab272da79ea59f54488f4a78da6426db/jiter-0.15.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:edebcf7d1f601199084bb6e844d7dc67e03e04f6ac786b0332d616635c4ff7a4", size = 310849, upload-time = "2026-05-19T10:06:51.944Z" }, + { url = "https://files.pythonhosted.org/packages/5d/8e/827be942883a4dc0862c48626ff41af3320b1902d136a0bf4b9041f2c567/jiter-0.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f924585cdacf631cd382b657966847bb537bf9ed0a6f9b991da5f05a631480f", size = 314991, upload-time = "2026-05-19T10:06:53.522Z" }, + { url = "https://files.pythonhosted.org/packages/6d/38/be2832be361ba1b9517c76f46d30b64e985be1dd43c974f4c3a4b1844436/jiter-0.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abbf258599526ad0326fe51e252e24f2bd6f24f1852681b4b78feda3808f1d18", size = 340843, upload-time = "2026-05-19T10:06:55.071Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d8/90f01fb83c0c7ba509303ec93e32a308fbfa167d264860b01c0fd0dbbd06/jiter-0.15.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c468136b8bd6bb18c8786e4236a1fa27362f24cb23450ba0cb204ab379b8e6f", size = 365116, upload-time = "2026-05-19T10:06:56.893Z" }, + { url = "https://files.pythonhosted.org/packages/91/38/94593d34f8c67a0b6f6cbc027f016ffa9780b3a858a7a86f6fd7a15bcc1e/jiter-0.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05906b93d72f03339e6bb7cf8dc10ebda64a0266126eed6beba79e20abcf5fd4", size = 457970, upload-time = "2026-05-19T10:06:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/df/04/d79962dd49d00c97e2a9b4cacea1947904d02135936960351f9a96d4c1a6/jiter-0.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30ce785d2adb8e32c3f7741442370a74834ec4c01f3c48f0750227a0b4ef27d6", size = 375744, upload-time = "2026-05-19T10:07:00.471Z" }, + { url = "https://files.pythonhosted.org/packages/c3/2e/5d37abe2be0e819c21e2338bebd410e481763ce526a9138c8c3652fa0123/jiter-0.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd73e3da91a0a722d67165e849ce2cdc10de0e0d48738c142be8c6c5f310f4c", size = 349609, upload-time = "2026-05-19T10:07:01.829Z" }, + { url = "https://files.pythonhosted.org/packages/7a/90/98768ad2ed90c1fda15d64157de2dfbf73c1c074d4b1bfaca915480bc7cf/jiter-0.15.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:ceb8fc27d38793f9c97149be8302720c5b22e5c195a37bf2c45dc36c4600a512", size = 354366, upload-time = "2026-05-19T10:07:03.587Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c4/fbfb806209f1fe4b7dccdfb07bc62bb044300734a945b06fd64db446ef6a/jiter-0.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d726e3ceeb337191324b49de298142f27c3ad10886341555d1d5315b5f252c6a", size = 393519, upload-time = "2026-05-19T10:07:05.08Z" }, + { url = "https://files.pythonhosted.org/packages/37/1c/b9c257cd70cb453b6d10f3ebf0402cdb11669ab455389096f09839670290/jiter-0.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2c8aea7781d2a372227871de4e1a1332aa96f5a89fd76c5e835dafdbad102887", size = 519952, upload-time = "2026-05-19T10:07:06.589Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1a/aa85027db7ab15829c12feebbc33b404f53fc399bd559d85fd0d6365ff0d/jiter-0.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cf4bd113a69c0a740e27cb962ce10630c36d2b8f59d759a651b955ee9d18a823", size = 550770, upload-time = "2026-05-19T10:07:08.228Z" }, + { url = "https://files.pythonhosted.org/packages/d4/54/8c3f65c8a5687925e84708f19d63f7f37d28e2b86a48d951702ad94424d8/jiter-0.15.0-cp310-cp310-win32.whl", hash = "sha256:d92a5cd21fdb083931d546c207aa29633787c5dc5b02daab2d32b843f88a2c53", size = 209303, upload-time = "2026-05-19T10:07:10.006Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/0528a1eb9f42dd2d8228a0711458628f35924d131f623eaebc35fd23d3d4/jiter-0.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:e58585a58209d72691ce2d62a9147445f5a87beb0bde97fde284c96ae392a3d1", size = 200404, upload-time = "2026-05-19T10:07:11.426Z" }, + { url = "https://files.pythonhosted.org/packages/e4/13/daa722f5765c393576f466378f9dfd29d77c9bed939e0688f96afa3601ea/jiter-0.15.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0f862193b8696249d22ec433e85fd2ab0ad9596bc3e45e6c0bc55e8aeba97be2", size = 310899, upload-time = "2026-05-19T10:07:12.89Z" }, + { url = "https://files.pythonhosted.org/packages/7f/82/2d2551829b082f4b6d82b9f939b031fb808a10aab1ec0664f82e150bb9a2/jiter-0.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1303d4d68a9b051ea90502402063ecf3807da00ad2affa19ca1ae3b90b3c5f67", size = 314963, upload-time = "2026-05-19T10:07:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0a/8b1a51466f7fe9f31dbe4bc7e0ca848674f9825e0f737b929b97e8c60aa7/jiter-0.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:392b8ab019e5502d08aff85c6272209c24bc2cbe706ea82a56368f524236614a", size = 341730, upload-time = "2026-05-19T10:07:15.869Z" }, + { url = "https://files.pythonhosted.org/packages/f6/2a/e71dea19822e2e404e83992a08c1d6b9b617bb944f28c9c2fbd85d02c91e/jiter-0.15.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:773b6eb282ce11ee19f05f6b2d4404fa308e5bbd353b0b80a0262caad6db2cd7", size = 366214, upload-time = "2026-05-19T10:07:17.259Z" }, + { url = "https://files.pythonhosted.org/packages/c4/59/97e1fa539d124a509a00ab7f669289d1c1d236ecabf12948a18f16c91082/jiter-0.15.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2c0c44d569ce0f2850f5c926f8caeb5f245fbc84475aeb36efccc2103e6dbd", size = 459527, upload-time = "2026-05-19T10:07:18.741Z" }, + { url = "https://files.pythonhosted.org/packages/d1/7a/4a68d331aef8cf2e2393c14a3aacb635c62aa86071b0229899fb5baaa907/jiter-0.15.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:032396229564bca02440396bd327710719f724f5e7b7e9f7a8eb3faa4a2c2281", size = 375451, upload-time = "2026-05-19T10:07:20.208Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/1c445c2b6f0e30a274dc8082e0c3c7825411cce80d726bccd697c98cc8d3/jiter-0.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d37768fce7f88dd2a8c6091f2325dea27d30d30d5c6e7a1c0f0af77723b708", size = 349428, upload-time = "2026-05-19T10:07:22.372Z" }, + { url = "https://files.pythonhosted.org/packages/00/94/e20d38984fc17a636371bffd2ae0f698124fdc8e75ef969cd2da6ba7cea7/jiter-0.15.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2c9cb907439d20bd0c7d7565ca01ee52234203208433749bae5b516907526928", size = 355405, upload-time = "2026-05-19T10:07:23.916Z" }, + { url = "https://files.pythonhosted.org/packages/94/fa/4d09f814779d0ea80a28ed8e4c6662ec9a4a8ecef0ac52190ebac6262d14/jiter-0.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9100ddbec09741cc66feb0fc6773f8bdbd0e3c345689368f260082ff85dcc0cd", size = 393688, upload-time = "2026-05-19T10:07:25.854Z" }, + { url = "https://files.pythonhosted.org/packages/54/9d/8eb5d4fb8bf7e93a75964a5da71a75c67c864baf7fa3f98598187b3c7e57/jiter-0.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ae1b0d82ac2d987f9ea512b1c9adfcc71a28de3dea3a6039b54d76cffda9901e", size = 520853, upload-time = "2026-05-19T10:07:27.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2c/5e07874e59e623a943a0acf1552a80d05b70f31b402287a8fc6d7ec634c7/jiter-0.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8020c99ec13a7db2b6f96cbe82ef4721c88b426a4892f27478044af0284615ef", size = 551016, upload-time = "2026-05-19T10:07:28.846Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/d2d34422143474cadc15b60d482b1c35683dbc5c63c24346ddd0df09bcaf/jiter-0.15.0-cp311-cp311-win32.whl", hash = "sha256:42bfb257930800cf43e7c62c832402c704ab60797c992faf88d20e903eac8f32", size = 209518, upload-time = "2026-05-19T10:07:30.431Z" }, + { url = "https://files.pythonhosted.org/packages/1d/7d/52778b930e5cc3e52a37d950b1c10494244308b4329b25a0ff0d88303a81/jiter-0.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:860a74063284a2ae9bfedd694f299cc2c68e2696c5f3d440cc9d18bb81b9dd04", size = 200565, upload-time = "2026-05-19T10:07:32.125Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4f/d9b4067feb69b3fa6eb0488e1b59e2ad5b463fe39f59e527eab2aca00bb0/jiter-0.15.0-cp311-cp311-win_arm64.whl", hash = "sha256:37a10c377ce3a4a85f4a67f28b7afe093154cde77eaf248a72e856aa08b4d865", size = 195488, upload-time = "2026-05-19T10:07:33.846Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" }, + { url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" }, + { url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" }, + { url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" }, + { url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" }, + { url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" }, + { url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" }, + { url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" }, + { url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" }, + { url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" }, + { url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" }, + { url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" }, + { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" }, + { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" }, + { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" }, + { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" }, + { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" }, + { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" }, + { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/079f350ebf7859d081de30aa890f9e3be68516f754f3ba32366ffff4dcee/jiter-0.15.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49", size = 308884, upload-time = "2026-05-19T10:08:31.667Z" }, + { url = "https://files.pythonhosted.org/packages/04/4e/a2c30a7f69b48c03b20935d647479106fe932f6e63f75faf53937197e05d/jiter-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86", size = 310028, upload-time = "2026-05-19T10:08:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/2e7cdfd3cf8ca967be38c48f5cf474d79f089efaf559a40f15984a77ae69/jiter-0.15.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f", size = 337485, upload-time = "2026-05-19T10:08:35.259Z" }, + { url = "https://files.pythonhosted.org/packages/9b/11/15a1aa28b120b8ee5b4f1fb894c125046225f09847738bd64233d3b84883/jiter-0.15.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e", size = 364223, upload-time = "2026-05-19T10:08:36.694Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/f442e8af5f3d0dcf47b39e83a0efd9ee45ea946aa6d04625dc3181eae3b6/jiter-0.15.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6", size = 456387, upload-time = "2026-05-19T10:08:38.143Z" }, + { url = "https://files.pythonhosted.org/packages/da/f4/37f2d2c9f64f49af7da652ed7532bb5a2372e588e6927c3fdd76f911db65/jiter-0.15.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9", size = 374461, upload-time = "2026-05-19T10:08:39.869Z" }, + { url = "https://files.pythonhosted.org/packages/60/28/edcfbbbf0cb15436f36664a8908a0df47ab9006298d4cd937dc08ea932d6/jiter-0.15.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c", size = 345924, upload-time = "2026-05-19T10:08:41.668Z" }, + { url = "https://files.pythonhosted.org/packages/47/13/89fba6398dab7f202b7278c4b4aac122399d2c0183971c4a57a3b7088df5/jiter-0.15.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd", size = 352283, upload-time = "2026-05-19T10:08:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/1b/da/0f6af8cef2c565a1ab44d970f268c43ccaa72707386ea6388e6fe2b6cd26/jiter-0.15.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89", size = 389985, upload-time = "2026-05-19T10:08:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ec/b9cb7d6d29e24ee14910266157d2a279d7a8f60ee0df7fa840882976ba64/jiter-0.15.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554", size = 517695, upload-time = "2026-05-19T10:08:46.486Z" }, + { url = "https://files.pythonhosted.org/packages/64/5e/6d1bda880723aae0ad86b4b763f044362448efe31e3e819635d41cb03451/jiter-0.15.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a", size = 548868, upload-time = "2026-05-19T10:08:48.026Z" }, + { url = "https://files.pythonhosted.org/packages/0c/72/7de501cf38dcacaf35098796f3a50e0f2e338baba18a58946c618544b809/jiter-0.15.0-cp314-cp314-win32.whl", hash = "sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec", size = 206380, upload-time = "2026-05-19T10:08:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a9/e19addf4b0c1bdce52c6da12351e6bc42c340c45e7c09e2158e46d293ccc/jiter-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558", size = 197687, upload-time = "2026-05-19T10:08:51.088Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c9/776b1db01db25fc6c1d58d1979a37b0a9fe787e5f5b1d062d2eaacb77923/jiter-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866", size = 192571, upload-time = "2026-05-19T10:08:52.451Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f6/45bb4670bacf300fd2c7abadbfb3af376e5f1b6ae75fd9bc069891d15870/jiter-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d", size = 317151, upload-time = "2026-05-19T10:08:53.867Z" }, + { url = "https://files.pythonhosted.org/packages/d7/68/ed635ad5acd7b73e454283083bbb7c8205ad10e88b0d9d7d793b09fe8226/jiter-0.15.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6", size = 341243, upload-time = "2026-05-19T10:08:55.383Z" }, + { url = "https://files.pythonhosted.org/packages/5d/db/3ff4176b817b8ea33879e71e13d8bc2b0d481a7ed3fe9e080f333d415c16/jiter-0.15.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995", size = 363629, upload-time = "2026-05-19T10:08:56.928Z" }, + { url = "https://files.pythonhosted.org/packages/ab/24/5f8270e0ba9c883582f96f722f8a0b58015c7ce1f8c6d4571cf394e99b6b/jiter-0.15.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8", size = 456198, upload-time = "2026-05-19T10:08:58.618Z" }, + { url = "https://files.pythonhosted.org/packages/45/5b/76fc02b0b5c54c3d18c60653156e2f76fde1816f9b4722db68d6ee2c897e/jiter-0.15.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5", size = 373710, upload-time = "2026-05-19T10:09:00.151Z" }, + { url = "https://files.pythonhosted.org/packages/c4/52/4310821b0ea9277994d3e1f49fc6a4b34e4800caebacb2c0af81da59a454/jiter-0.15.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b", size = 349901, upload-time = "2026-05-19T10:09:01.621Z" }, + { url = "https://files.pythonhosted.org/packages/93/fe/67648c35b3594fba8854ac64cc8a826d8bcd18324bbdb53d77697c60b6ef/jiter-0.15.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8", size = 352438, upload-time = "2026-05-19T10:09:03.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/0a1879d07ad6b3e025a2750027363452ced93c2d16d1c9d4b153ffd51c91/jiter-0.15.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec", size = 388152, upload-time = "2026-05-19T10:09:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/c1/78/46c6f6b56ba85c90021f4afd72ed42f691f8f84daacb5fe27277070e3858/jiter-0.15.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e", size = 517707, upload-time = "2026-05-19T10:09:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/ca/cb/720662d4c88fcad606e826fef5424365527ba43ce4868a479aed8f8c507e/jiter-0.15.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5", size = 548241, upload-time = "2026-05-19T10:09:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/60/e3/935b8034fd143f21125c87d51404a9e0e1449186a494405721ff5d1d695e/jiter-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52", size = 207950, upload-time = "2026-05-19T10:09:09.616Z" }, + { url = "https://files.pythonhosted.org/packages/93/59/984fd9ece895953dad3e0880a650e766f5a2da2c5514f0eafdaaabbeb5f9/jiter-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854", size = 200055, upload-time = "2026-05-19T10:09:11.367Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/cf8d779feb133a27a2e3bc833bccb9e13aa332cdf820497ebf72c10ce8c3/jiter-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0", size = 191244, upload-time = "2026-05-19T10:09:12.74Z" }, + { url = "https://files.pythonhosted.org/packages/fd/f5/33aa7dcd6b470db4c13098ea464e67bf4c32939909e21ce435105924c78a/jiter-0.15.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:04b400bbf8c9efb03d9bdd976475c919c1d85593b04b9fff7ae234065daf87ae", size = 313805, upload-time = "2026-05-19T10:09:14.474Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/dd0d3f712e2045302a66b1b54dd3d9a9fb095c1c74e60537f0f32760074b/jiter-0.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25ffbe229aa8cd98c28879d8aa1a6e34ae77992ab984a65fba800859dab16269", size = 311805, upload-time = "2026-05-19T10:09:16.359Z" }, + { url = "https://files.pythonhosted.org/packages/12/c4/1bd1b4e1da9a12a37e92eeacfd850452de208c95265989deda3fa0d172e1/jiter-0.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5607e6013ed7e6b0ec9661e467b7ffde0aa7ab36833a04850f26fcf88ed4845b", size = 343962, upload-time = "2026-05-19T10:09:17.961Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/c5bcc400c4c5fa4a57d8d6ab6fc468d762f800a94b9be3051c29c267de6e/jiter-0.15.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50164d7610c00e7cd913a873fce30b6beeebf4b37e53983e33f22de4c900f6b8", size = 367715, upload-time = "2026-05-19T10:09:19.387Z" }, + { url = "https://files.pythonhosted.org/packages/ee/42/58c2951603b7beb896a129a2fa0b857149faf71f97f24ae63f358f99bf1b/jiter-0.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ab596fa3837e91e7e6a31b5f639988bfc6a35d1f915ac3932d946062219d588f", size = 460974, upload-time = "2026-05-19T10:09:20.935Z" }, + { url = "https://files.pythonhosted.org/packages/5d/0b/1f325e1c0d83a3fd9c43534755f35dd35d018a6bc78022cd29426182087d/jiter-0.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72d8af5c1013656a8870c866660627d1a75bc185814ee022c8533caa1de88ae", size = 378105, upload-time = "2026-05-19T10:09:22.431Z" }, + { url = "https://files.pythonhosted.org/packages/06/ee/8f2da5bd1fc62df6d78890e5c98f6bc1140566a71c7a06b4074955322425/jiter-0.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c84c1b7be454b0c16f8499b4ebfbfd82ea5cca6527cceefcbbc06a7557b5ed2e", size = 353195, upload-time = "2026-05-19T10:09:24.168Z" }, + { url = "https://files.pythonhosted.org/packages/fa/81/08911d1ebf7d2780b4438b0073499668869dde9b95bf83fe5e3a54ef4163/jiter-0.15.0-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:d636d5095155afd364247f65070fab7beda13498d7ff4de331046e704ab9657f", size = 357865, upload-time = "2026-05-19T10:09:25.761Z" }, + { url = "https://files.pythonhosted.org/packages/f2/68/4d3db1ca5701ed59f0c5386e59920c5eb389a5aec79e7749c6768428f304/jiter-0.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7d3d6683288c11cbab50e865f2e2f13950179aa45410e30b2cfbd3fb7b0177bf", size = 397243, upload-time = "2026-05-19T10:09:27.47Z" }, + { url = "https://files.pythonhosted.org/packages/7d/7c/cfd0ab10ac58c5c6a711ef17e6b3c13938a8e4c2db97d4b4bfd5aaf529b7/jiter-0.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7ce8902f939970048b233087082e7bb829db29375811c7ad50687b8624c6fd08", size = 522617, upload-time = "2026-05-19T10:09:29.065Z" }, + { url = "https://files.pythonhosted.org/packages/90/a7/2ec0105572aee537c3bbac6be1dcefb12f59d7f029c4fd28a04a47fea25c/jiter-0.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4363818355dbc70ae1a8e9eaba9de350d93ede4ff6992b8f8eb8cbb6e5122d42", size = 553940, upload-time = "2026-05-19T10:09:30.9Z" }, + { url = "https://files.pythonhosted.org/packages/98/59/ba1ec23b16a532ffcafce111827b65cc77e79744de63c81be189262dbe9f/jiter-0.15.0-cp39-cp39-win32.whl", hash = "sha256:8f7e9bc0f1135039b22ee6eab588d42df1ce55842b30740a352885eb267bd941", size = 211562, upload-time = "2026-05-19T10:09:32.752Z" }, + { url = "https://files.pythonhosted.org/packages/af/38/1749bf02cb91be2dc504c2c8a3fc46815fc3c06f6532e058536406cd436b/jiter-0.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:1c15024a3d892223b18f597c86d59387249dc396590844ce6b9f6131d1093bae", size = 203226, upload-time = "2026-05-19T10:09:34.222Z" }, + { url = "https://files.pythonhosted.org/packages/65/43/1fc62172aa98b50a7de9a25554060db510f85c89cfbed0dfe13e1907a139/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:411fa4dfa5a7ae3d11491027ffb9beadec3996010a986862db70d91abba1c750", size = 305585, upload-time = "2026-05-19T10:09:35.995Z" }, + { url = "https://files.pythonhosted.org/packages/e8/c4/dd58fcd9e2df83666e5c1c1347bef58ce919cd8efc3ffa38aeea62ce493b/jiter-0.15.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:2b0074e2f56eb2dacca1689760fd2852a068f85a0547a157b82cb4cafeb6768b", size = 306936, upload-time = "2026-05-19T10:09:37.435Z" }, + { url = "https://files.pythonhosted.org/packages/39/86/b695e16f1180c07f43ea98e73ecd21cf63fa2e1b0c1103739013784d11ae/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:913d02d29c9606643418d9ccfc3b72492ab25a6bf7889934e09a3490f8d3438b", size = 342453, upload-time = "2026-05-19T10:09:39.294Z" }, + { url = "https://files.pythonhosted.org/packages/34/56/55d76614af37fe3f22a3347d1e410d2a15da581997cb2da499a625000bb5/jiter-0.15.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b15d3ec9b0449c40e85319bdb4caa8b77ab526e74f5532ed94bec15e2f66822c", size = 345606, upload-time = "2026-05-19T10:09:40.727Z" }, + { url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/74525ebe3eb5fddcd6735fc03cbea3feeed4122b53bc798ac32d297ac9ae/multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", size = 77107, upload-time = "2026-01-26T02:46:12.608Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9a/ce8744e777a74b3050b1bf56be3eed1053b3457302ea055f1ea437200a23/multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", size = 44943, upload-time = "2026-01-26T02:46:14.016Z" }, + { url = "https://files.pythonhosted.org/packages/83/9c/1d2a283d9c6f31e260cb6c2fccadc3edcf6c4c14ee0929cd2af4d2606dd7/multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", size = 44603, upload-time = "2026-01-26T02:46:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/3b186201671583d8e8d6d79c07481a5aafd0ba7575e3d8566baec80c1e82/multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", size = 240573, upload-time = "2026-01-26T02:46:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/42/7d/a52f5d4d0754311d1ac78478e34dff88de71259a8585e05ee14e5f877caf/multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", size = 240106, upload-time = "2026-01-26T02:46:18.432Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/d80118e6c30ff55b7d171bdc5520aad4b9626e657520b8d7c8ca8c2fad12/multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", size = 219418, upload-time = "2026-01-26T02:46:20.526Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/896e60b3457f194de77c7de64f9acce9f75da0518a5230ce1df534f6747b/multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", size = 252124, upload-time = "2026-01-26T02:46:22.157Z" }, + { url = "https://files.pythonhosted.org/packages/f4/de/ba6b30447c36a37078d0ba604aa12c1a52887af0c355236ca6e0a9d5286f/multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", size = 249402, upload-time = "2026-01-26T02:46:23.718Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b2/50a383c96230e432895a2fd3bcfe1b65785899598259d871d5de6b93180c/multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", size = 240346, upload-time = "2026-01-26T02:46:25.393Z" }, + { url = "https://files.pythonhosted.org/packages/89/37/16d391fd8da544b1489306e38a46785fa41dd0f0ef766837ed7d4676dde0/multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", size = 237010, upload-time = "2026-01-26T02:46:27.408Z" }, + { url = "https://files.pythonhosted.org/packages/b0/24/3152ee026eda86d5d3e3685182911e6951af7a016579da931080ce6ac9ad/multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", size = 232018, upload-time = "2026-01-26T02:46:29.941Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1f/48d3c27a72be7fd23a55d8847193c459959bf35a5bb5844530dab00b739b/multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", size = 241498, upload-time = "2026-01-26T02:46:32.052Z" }, + { url = "https://files.pythonhosted.org/packages/1a/45/413643ae2952d0decdf6c1250f86d08a43e143271441e81027e38d598bd7/multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", size = 247957, upload-time = "2026-01-26T02:46:33.666Z" }, + { url = "https://files.pythonhosted.org/packages/50/f8/f1d0ac23df15e0470776388bdb261506f63af1f81d28bacb5e262d6e12b6/multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", size = 241651, upload-time = "2026-01-26T02:46:35.7Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c9/1a2a18f383cf129add66b6c36b75c3911a7ba95cf26cb141482de085cc12/multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", size = 236371, upload-time = "2026-01-26T02:46:37.37Z" }, + { url = "https://files.pythonhosted.org/packages/bb/aa/77d87e3fca31325b87e0eb72d5fe9a7472dcb51391a42df7ac1f3842f6c0/multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", size = 41426, upload-time = "2026-01-26T02:46:39.026Z" }, + { url = "https://files.pythonhosted.org/packages/e3/b3/e8863e6a2da15a9d7e98976ff402e871b7352c76566df6c18d0378e0d9cf/multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", size = 46180, upload-time = "2026-01-26T02:46:40.422Z" }, + { url = "https://files.pythonhosted.org/packages/93/d3/dd4fa951ad5b5fa216bf30054d705683d13405eea7459833d78f31b74c9c/multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", size = 43231, upload-time = "2026-01-26T02:46:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, + { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, + { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, + { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, + { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, + { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, + { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, + { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, + { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "openai" +version = "2.43.0" +source = { editable = "." } +dependencies = [ + { name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "anyio", version = "4.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] + +[package.optional-dependencies] +aiohttp = [ + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "aiohttp", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "httpx-aiohttp" }, +] +datalib = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pandas-stubs", version = "2.2.2.240807", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "pandas-stubs", version = "2.3.3.260113", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "pandas-stubs", version = "3.0.3.260530", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +realtime = [ + { name = "websockets" }, +] +voice-helpers = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sounddevice" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", marker = "extra == 'aiohttp'" }, + { name = "anyio", specifier = ">=3.5.0,<5" }, + { name = "distro", specifier = ">=1.7.0,<2" }, + { name = "httpx", specifier = ">=0.23.0,<1" }, + { name = "httpx-aiohttp", marker = "extra == 'aiohttp'", specifier = ">=0.1.9" }, + { name = "jiter", specifier = ">=0.10.0,<1" }, + { name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" }, + { name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" }, + { name = "pandas", marker = "extra == 'datalib'", specifier = ">=1.2.3" }, + { name = "pandas-stubs", marker = "extra == 'datalib'", specifier = ">=1.1.0.11" }, + { name = "pydantic", specifier = ">=1.9.0,<3" }, + { name = "sniffio" }, + { name = "sounddevice", marker = "extra == 'voice-helpers'", specifier = ">=0.5.1" }, + { name = "tqdm", specifier = ">4" }, + { name = "typing-extensions", specifier = ">=4.14,<5" }, + { name = "websockets", marker = "extra == 'realtime'", specifier = ">=13,<16" }, +] +provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers"] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, + { url = "https://files.pythonhosted.org/packages/56/b4/52eeb530a99e2a4c55ffcd352772b599ed4473a0f892d127f4147cf0f88e/pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2", size = 11567720, upload-time = "2025-09-29T23:33:06.209Z" }, + { url = "https://files.pythonhosted.org/packages/48/4a/2d8b67632a021bced649ba940455ed441ca854e57d6e7658a6024587b083/pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8", size = 10810302, upload-time = "2025-09-29T23:33:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/13/e6/d2465010ee0569a245c975dc6967b801887068bc893e908239b1f4b6c1ac/pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff", size = 12154874, upload-time = "2025-09-29T23:33:49.939Z" }, + { url = "https://files.pythonhosted.org/packages/1f/18/aae8c0aa69a386a3255940e9317f793808ea79d0a525a97a903366bb2569/pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29", size = 12790141, upload-time = "2025-09-29T23:34:05.655Z" }, + { url = "https://files.pythonhosted.org/packages/f7/26/617f98de789de00c2a444fbe6301bb19e66556ac78cff933d2c98f62f2b4/pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73", size = 13208697, upload-time = "2025-09-29T23:34:21.835Z" }, + { url = "https://files.pythonhosted.org/packages/b9/fb/25709afa4552042bd0e15717c75e9b4a2294c3dc4f7e6ea50f03c5136600/pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9", size = 13879233, upload-time = "2025-09-29T23:34:35.079Z" }, + { url = "https://files.pythonhosted.org/packages/98/af/7be05277859a7bc399da8ba68b88c96b27b48740b6cf49688899c6eb4176/pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa", size = 11359119, upload-time = "2025-09-29T23:34:46.339Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/16/b5c76b838fd9bf6ce84d3a53346b8874ec05c5f0040d75ef2c320100cd2a/pandas-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:455f6f8139d4282188f526868dbc3c828470e88a3d9d59a891bd46a455f21b98", size = 10338495, upload-time = "2026-05-11T18:52:11.558Z" }, + { url = "https://files.pythonhosted.org/packages/5a/b0/a4ffc4ae74d2d822200dcc46898987d8eb6032d1e2b219cae39da6f5cbcc/pandas-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e15135e2ee5df1063313e2425ceef8ac0f4ae775893815b0923651b806a5639", size = 9938250, upload-time = "2026-05-11T18:52:17.005Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b2/3323601a52caee42c019e370090ca4544b241437240ca04f786cce82b0cf/pandas-3.0.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05f1f1752b8533ea03f7f39a9c15b1a058d067bb48f4748948e7a8691e0510f2", size = 10770558, upload-time = "2026-05-11T18:52:19.865Z" }, + { url = "https://files.pythonhosted.org/packages/32/f1/bbecd2f867b97abebe0f9b53d750f862251b40337e061b36676ded3d920f/pandas-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a1e45c80cceb3b4a21bc5939d52e8cbd8d9b7305309219d59e9754d9ce09e27", size = 11274611, upload-time = "2026-05-11T18:52:22.622Z" }, + { url = "https://files.pythonhosted.org/packages/7f/4f/eafabf2d5fae5adf143b4d18d3706c5efdc368a7c4eb1ee8a3eddabbd0f6/pandas-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:14da8316da4d0c5a77618425996bfb1248ca87fc2c1486e6fde4652bd18b5824", size = 11784670, upload-time = "2026-05-11T18:52:25.4Z" }, + { url = "https://files.pythonhosted.org/packages/49/44/1eb20389301b57b19cc099a1c2f662501f72f08a65f912d05822613c1532/pandas-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a55066a0505dae0ba2b50a46637db34b46f9094c65c5d4800794ef6335010938", size = 12353708, upload-time = "2026-05-11T18:52:28.139Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/c321f13b5ba1819fc8dca456c7fce578da2dcfecff1abbf0eaddf8406c0f/pandas-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6674ab18ad8c57802867264b00e15e7bb904700cdd9046e3b2fa1fce237439ea", size = 9907609, upload-time = "2026-05-11T18:52:30.982Z" }, + { url = "https://files.pythonhosted.org/packages/53/85/1b7f563ebc6357c27233a02a96b589bcce1fa9c6eb89fb4f0e56421d277e/pandas-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:5cc09a68b3120e0f54870dede8287a7bb1fa463907e4fcec1ea77cab6179bf7a", size = 9165596, upload-time = "2026-05-11T18:52:33.334Z" }, + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, +] + +[[package]] +name = "pandas-stubs" +version = "2.2.2.240807" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "types-pytz", version = "2025.2.0.20251108", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/df/0da95bc75c76f1e012e0bc0b76da31faaf4254e94b9870f25e6311145e98/pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93", size = 103095, upload-time = "2024-08-07T12:30:54.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/f9/22c91632ea1b4c6165952f677bf9ad95f9ac36ffd7ef3e6450144e6d8b1a/pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e", size = 157069, upload-time = "2024-08-07T12:30:51.868Z" }, +] + +[[package]] +name = "pandas-stubs" +version = "2.3.3.260113" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "types-pytz", version = "2026.2.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c6/df1fe324248424f77b89371116dab5243db7f052c32cc9fe7442ad9c5f75/pandas_stubs-2.3.3.260113-py3-none-any.whl", hash = "sha256:ec070b5c576e1badf12544ae50385872f0631fc35d99d00dc598c2954ec564d3", size = 168246, upload-time = "2026-01-13T22:30:15.244Z" }, +] + +[[package]] +name = "pandas-stubs" +version = "3.0.3.260530" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/aa/c41a8a0ff86fd85dbb3ec0c1f3fa488ca64a8b5f82654ae1b07d84acefe5/pandas_stubs-3.0.3.260530.tar.gz", hash = "sha256:d1efe47b2e5a312c047d7feabec5cb7a55365747983420077e9fcbe9ab74f714", size = 113183, upload-time = "2026-05-30T17:47:40.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/e0/99ec5b02203c4e9ce878bc63d8caa06ac1f891e4d63bded9a5ced70fcb4f/pandas_stubs-3.0.3.260530-py3-none-any.whl", hash = "sha256:a6277eb1c8cebf48d9b2413fcd2e9a6b4ff479c934a223c29eacbc3058c4cb55", size = 173780, upload-time = "2026-05-30T17:47:39.13Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, + { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, + { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, + { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, + { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, + { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/56/030b7b4719d53085722893e0009dffb9236aa10bca1b12121bdc5626ef16/propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b", size = 93417, upload-time = "2026-05-08T20:59:15.597Z" }, + { url = "https://files.pythonhosted.org/packages/1a/55/1140a8e067b8ec093a18a4ae7bb0045d9db65da38a08618ddc5e2f1994aa/propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c", size = 53847, upload-time = "2026-05-08T20:59:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/20/42/0e7443c90310498561addf346e7d57fe3c6ba1914e1ba938b5464c7bbfd2/propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb", size = 53512, upload-time = "2026-05-08T20:59:18.64Z" }, + { url = "https://files.pythonhosted.org/packages/b7/db/cf51a71bab2009517d1a7f0ee07657e3bd446c4d69f67e6966cf17bcf956/propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e", size = 58068, upload-time = "2026-05-08T20:59:20.683Z" }, + { url = "https://files.pythonhosted.org/packages/b7/43/39b6bdee9699fa1e1641c519feeb64a67e2a9f93bb465c70776b37a7333f/propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e", size = 61020, upload-time = "2026-05-08T20:59:22.112Z" }, + { url = "https://files.pythonhosted.org/packages/26/0b/843726fbb0a29a8c5684fdb25971823638399f31e52e9d1f06a02dc9aa6b/propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b", size = 62732, upload-time = "2026-05-08T20:59:23.805Z" }, + { url = "https://files.pythonhosted.org/packages/39/6e/899fed76dc1942b8a64193a4f059d7f1a2c7ef65085e8a9366ed8ec0d199/propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d", size = 60140, upload-time = "2026-05-08T20:59:25.389Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/3da4be9b5b879219ad234aa535b3dd4a080ed1ad48d3a73ca07a9e798f22/propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d", size = 60400, upload-time = "2026-05-08T20:59:27.238Z" }, + { url = "https://files.pythonhosted.org/packages/60/2f/09b72b874a9aa0044faf52a69807a6ed618e267ceaa9ec4a63195fa5b504/propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0", size = 58155, upload-time = "2026-05-08T20:59:28.48Z" }, + { url = "https://files.pythonhosted.org/packages/8a/37/97489848c54c95578045473954f10956d619ce6a09e7ac137b71cdcb698b/propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b", size = 57037, upload-time = "2026-05-08T20:59:30.146Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/6c695285ccfc49012743ee9c98212b8c5dd0aed7b63cfd816d4a0f7a1601/propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf", size = 61103, upload-time = "2026-05-08T20:59:31.626Z" }, + { url = "https://files.pythonhosted.org/packages/98/a9/1e500401ca593b0bdb6bf75a70bc2d723835fd53360edff6af70692c7546/propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf", size = 60394, upload-time = "2026-05-08T20:59:32.829Z" }, + { url = "https://files.pythonhosted.org/packages/1f/87/f638b6e375eae0f30a1a2325d8b34fd85fdc785bb9960cf805f3bf1ec69a/propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e", size = 63084, upload-time = "2026-05-08T20:59:35.964Z" }, + { url = "https://files.pythonhosted.org/packages/f6/18/884573f5d97b6d9eba68de759a82c901b7e39d7904d30f7b8d58d42d2a12/propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274", size = 60999, upload-time = "2026-05-08T20:59:38.481Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1a/c3915eb059ceec9e758a56e4cfd955292bc0f201be2176a46b76d94b303a/propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe", size = 39036, upload-time = "2026-05-08T20:59:40.323Z" }, + { url = "https://files.pythonhosted.org/packages/5b/02/1dfd5607501a602d19c1c449d2d193b7d1c611f9246b4059026a1189a80e/propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d", size = 42190, upload-time = "2026-05-08T20:59:42.232Z" }, + { url = "https://files.pythonhosted.org/packages/57/93/f71588ad08b3e6f4b555b5ef215808a3c02b042d0151ad82fa6f15be677a/propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5", size = 38545, upload-time = "2026-05-08T20:59:44.087Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744, upload-time = "2026-05-08T20:59:45.799Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033, upload-time = "2026-05-08T20:59:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754, upload-time = "2026-05-08T20:59:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573, upload-time = "2026-05-08T20:59:50.778Z" }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645, upload-time = "2026-05-08T20:59:52.227Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563, upload-time = "2026-05-08T20:59:53.866Z" }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888, upload-time = "2026-05-08T20:59:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253, upload-time = "2026-05-08T20:59:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558, upload-time = "2026-05-08T20:59:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007, upload-time = "2026-05-08T20:59:59.837Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355, upload-time = "2026-05-08T21:00:01.144Z" }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057, upload-time = "2026-05-08T21:00:02.401Z" }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938, upload-time = "2026-05-08T21:00:03.638Z" }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731, upload-time = "2026-05-08T21:00:04.881Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966, upload-time = "2026-05-08T21:00:06.511Z" }, + { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135, upload-time = "2026-05-08T21:00:08.088Z" }, + { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381, upload-time = "2026-05-08T21:00:09.692Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "pycparser" +version = "2.23" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/08/f1ba952f1c8ae5581c70fa9c6da89f247b83e3dd8c09c035d5d7931fc23d/pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4", size = 2113146, upload-time = "2026-05-06T13:37:36.537Z" }, + { url = "https://files.pythonhosted.org/packages/56/c6/65f646c7ff09bd257f660434adb45c4dfcbbcebcc030562fecf6f5bf887d/pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5", size = 1949769, upload-time = "2026-05-06T13:37:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/64/ba/bfb1d928fd5b49e1258935ff104ae356e9fd89384a55bf9f847e9193ad40/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba", size = 1974958, upload-time = "2026-05-06T13:37:28.611Z" }, + { url = "https://files.pythonhosted.org/packages/4e/74/76223bfb117b64af743c9b6670d1364516f5c0604f96b48f3272f6af6cc6/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b", size = 2042118, upload-time = "2026-05-06T13:36:55.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7b/848732968bc8f48f3187542f08358b9d842db564147b256669426ebb1652/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c", size = 2222876, upload-time = "2026-05-06T13:38:25.455Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2f/e90b63ee2e14bd8d3db8f705a6d75d64e6ee1b7c2c8833747ce706e1e0ce/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50", size = 2286703, upload-time = "2026-05-06T13:37:53.304Z" }, + { url = "https://files.pythonhosted.org/packages/ba/1e/acc4d70f88a0a277e4a1fa77ebb985ceabaf900430f875bf9338e11c9420/pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd", size = 2092042, upload-time = "2026-05-06T13:38:46.981Z" }, + { url = "https://files.pythonhosted.org/packages/a9/da/0a422b57bf8504102bf3c4ccea9c41bab5a5cee6a54650acf8faf67f5a24/pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01", size = 2117231, upload-time = "2026-05-06T13:39:23.146Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2a/2ac13c3af305843e23c5078c53d135656b3f05a2fd78cb7bbbb12e97b473/pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d", size = 2168388, upload-time = "2026-05-06T13:40:08.06Z" }, + { url = "https://files.pythonhosted.org/packages/72/04/2beacf7e1607e93eefe4aed1b4709f079b905fb77530179d4f7c71745f22/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4", size = 2184769, upload-time = "2026-05-06T13:38:13.901Z" }, + { url = "https://files.pythonhosted.org/packages/9e/29/d2b9fd9f539133548eaf622c06a4ce176cb46ac59f32d0359c4abc0de047/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f", size = 2319312, upload-time = "2026-05-06T13:39:08.24Z" }, + { url = "https://files.pythonhosted.org/packages/7c/af/0f7a5b85fec6075bea96e3ef9187de38fccced0de92c1e7feda8d5cc7bb9/pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39", size = 2361817, upload-time = "2026-05-06T13:38:43.2Z" }, + { url = "https://files.pythonhosted.org/packages/25/a4/73363fec545fd3ec025490bdda2743c56d0dd5b6266b1a53bbe9e4265375/pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d", size = 1987085, upload-time = "2026-05-06T13:39:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/01/aa/62f082da2c91fac1c234bc9ee0066257ce83f0604abd72e4c9d5991f2d84/pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf", size = 2074311, upload-time = "2026-05-06T13:39:59.922Z" }, + { url = "https://files.pythonhosted.org/packages/5c/fa/6d7708d2cfc1a832acb6aeb0cd16e801902df8a0f583bb3b4b527fde022e/pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594", size = 2111872, upload-time = "2026-05-06T13:40:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6f/aa064a3e74b5745afbdf250594f38e7ead05e2d651bcb35994b9417a0d4d/pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c", size = 1948255, upload-time = "2026-05-06T13:39:12.574Z" }, + { url = "https://files.pythonhosted.org/packages/43/3a/41114a9f7569b84b4d84e7a018c57c56347dac30c0d4a872946ec4e36c46/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826", size = 1972827, upload-time = "2026-05-06T13:38:19.841Z" }, + { url = "https://files.pythonhosted.org/packages/ef/25/1ab42e8048fe551934d9884e8d64daa7e990ad386f310a15981aeb6a5b08/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04", size = 2041051, upload-time = "2026-05-06T13:38:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/94/c2/1a934597ddf08da410385b3b7aae91956a5a76c635effef456074fad7e88/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e", size = 2221314, upload-time = "2026-05-06T13:40:13.089Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/9e8ad178c9c4df27ad3c8f25d1fe2a7ab0d2ba0559fad4aee5d3d1f16771/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3", size = 2285146, upload-time = "2026-05-06T13:38:59.224Z" }, + { url = "https://files.pythonhosted.org/packages/80/50/540cd3aeefc041beb111125c4bff779831a2111fc6b15a9138cda277d32c/pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4", size = 2089685, upload-time = "2026-05-06T13:38:17.762Z" }, + { url = "https://files.pythonhosted.org/packages/6b/a4/b440ad35f05f6a38f89fa0f149accb3f0e02be94ca5e15f3c449a61b4bc9/pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398", size = 2115420, upload-time = "2026-05-06T13:37:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/99/61/de4f55db8dfd57bfdfa9a12ec90fe1b57c4f41062f7ca86f08586b3e0ac0/pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3", size = 2165122, upload-time = "2026-05-06T13:37:01.167Z" }, + { url = "https://files.pythonhosted.org/packages/f7/52/7c529d7bdb2d1068bd52f51fe32572c8301f9a4febf1948f10639f1436f5/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848", size = 2182573, upload-time = "2026-05-06T13:38:45.04Z" }, + { url = "https://files.pythonhosted.org/packages/37/b3/7c40325848ba78247f2812dcf9c7274e38cd801820ca6dd9fe63bcfb0eb4/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3", size = 2317139, upload-time = "2026-05-06T13:37:15.539Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/f913f81a657c865b75da6c0dbed79876073c2a43b5bd9edbe8da785e4d49/pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109", size = 2360433, upload-time = "2026-05-06T13:37:30.099Z" }, + { url = "https://files.pythonhosted.org/packages/c4/67/6acaa1be2567f9256b056d8477158cac7240813956ce86e49deae8e173b4/pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda", size = 1985513, upload-time = "2026-05-06T13:38:15.669Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e6/c505f83dfeda9a2e5c995cfd872949e4d05e12f7feb3dca72f633daefa94/pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33", size = 2071114, upload-time = "2026-05-06T13:40:35.416Z" }, + { url = "https://files.pythonhosted.org/packages/0f/da/7a263a96d965d9d0df5e8de8a475f33495451117035b09acb110288c381f/pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d", size = 2044298, upload-time = "2026-05-06T13:38:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/5d/00/13a0c039569d1e583779ee1b8d7df6bfe275a0db83fcae14f01d6856c16e/pydantic_core-2.46.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae", size = 2115337, upload-time = "2026-05-06T13:38:37.741Z" }, + { url = "https://files.pythonhosted.org/packages/41/60/e70fa1ee03e243bdfd4b1fddf1e1f2a8fba681df3034b51b9376c0fb5bf5/pydantic_core-2.46.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201", size = 1957976, upload-time = "2026-05-06T13:37:33.478Z" }, + { url = "https://files.pythonhosted.org/packages/11/9a/78fb5f2ea849f767ea802de8b4e8f5a0c4a48ddbe4bc66bd19ac2f55a01c/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0", size = 1979390, upload-time = "2026-05-06T13:36:52.419Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7d/3acfdcd000bad9735de0430a88355948469781f62cb841fd63e8a307e80e/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15", size = 2043263, upload-time = "2026-05-06T13:39:54.798Z" }, + { url = "https://files.pythonhosted.org/packages/35/60/1325e5a8d7f9697416481c7f7c1c304738d6b961a7fd1ea0f054ce0f14fb/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76", size = 2225708, upload-time = "2026-05-06T13:40:24.887Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b0/9ec8c38f33b26db0b612cb7fd165bb0a370773710432a2a74fa31287b430/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49", size = 2288494, upload-time = "2026-05-06T13:38:00.091Z" }, + { url = "https://files.pythonhosted.org/packages/65/05/497446a9586d1b2d24ee25ebe208beb15388f1875d783e1e014055d150ac/pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928", size = 2095629, upload-time = "2026-05-06T13:38:23.632Z" }, + { url = "https://files.pythonhosted.org/packages/93/d9/cd5fa98f9d94f9294c15459396c8a2383c164469e679ac178d6d42cfee6b/pydantic_core-2.46.4-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066", size = 2119309, upload-time = "2026-05-06T13:39:50.144Z" }, + { url = "https://files.pythonhosted.org/packages/20/1b/64cec655451ddbf3976df5dc9706b240df4fdaebdeebeadd4f59a8dab926/pydantic_core-2.46.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6", size = 2170216, upload-time = "2026-05-06T13:39:14.561Z" }, + { url = "https://files.pythonhosted.org/packages/2a/21/fe9f039138c9ea3be10ccdb6ec490acb54dcbef5a5e96dbdf1411f82b929/pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9", size = 2186726, upload-time = "2026-05-06T13:37:51.597Z" }, + { url = "https://files.pythonhosted.org/packages/44/cb/19ca0da64821d1aefcef65f253aa9ecbdd0dde360f607d0f9b3d95db2b4e/pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29", size = 2320400, upload-time = "2026-05-06T13:39:36.29Z" }, + { url = "https://files.pythonhosted.org/packages/cd/14/fe3fbf6e845bf2080dc2f282d75085ddf79d037b35634ecde68f33c217b4/pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9", size = 2363318, upload-time = "2026-05-06T13:38:53.039Z" }, + { url = "https://files.pythonhosted.org/packages/62/88/60b110889507a426eecf626f7536566cb290ada71147eff49b6e2724ca62/pydantic_core-2.46.4-cp39-cp39-win32.whl", hash = "sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1", size = 1988880, upload-time = "2026-05-06T13:39:16.572Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d6/8ede2f98f17e1e4e127d37be0eced4eee931a511c62cd68af50e1b25bfa9/pydantic_core-2.46.4-cp39-cp39-win_amd64.whl", hash = "sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac", size = 2079257, upload-time = "2026-05-06T13:39:38.498Z" }, + { url = "https://files.pythonhosted.org/packages/ee/a4/73995fd4ebbb46ba0ee51e6fa049b8f02c40daebb762208feda8a6b7894d/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c", size = 2111589, upload-time = "2026-05-06T13:37:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7f/f37d3a5e8bfcc2e403f5c57a730f2d815693fb42119e8ea48b3789335af1/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b", size = 1944552, upload-time = "2026-05-06T13:36:56.717Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/d7eb777b3ff43e8433a4efb39a17aa8fd98a4ee8561a24a67ef5db07b2d6/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b", size = 1982984, upload-time = "2026-05-06T13:39:06.207Z" }, + { url = "https://files.pythonhosted.org/packages/63/87/70b9f40170a81afd55ca26c9b2acb25c20d64bcfbf888fafecb3ba077d4c/pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea", size = 2138417, upload-time = "2026-05-06T13:39:45.476Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/cb/428de0385b6c8d44b716feba566abfacfbd23ee3c4439faa789a1456242f/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0", size = 2112782, upload-time = "2026-05-06T13:37:04.016Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b5/6a17bdadd0fc1f170adfd05a20d37c832f52b117b4d9131da1f41bb097ce/pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7", size = 1952146, upload-time = "2026-05-06T13:39:43.092Z" }, + { url = "https://files.pythonhosted.org/packages/2a/dc/03734d80e362cd43ef65428e9de77c730ce7f2f11c60d2b1e1b39f0fbf99/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2", size = 2134492, upload-time = "2026-05-06T13:36:58.124Z" }, + { url = "https://files.pythonhosted.org/packages/de/df/5e5ffc085ed07cc22d298134d3d911c63e91f6a0eb91fe646750a3209910/pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9", size = 2156604, upload-time = "2026-05-06T13:37:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/81/44/6e112a4253e56f5705467cbab7ab5e91ee7398ba3d56d358635958893d3e/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf", size = 2183828, upload-time = "2026-05-06T13:37:43.053Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/5565071e937d8e752842ac241463944c9eb14c87e2d269f2658a5bd05e98/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30", size = 2310000, upload-time = "2026-05-06T13:37:56.694Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c3/66883a5cec183e7fba4d024b4cbbe61851a63750ef606b0afecc46d1f2bf/pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc", size = 2361286, upload-time = "2026-05-06T13:40:05.667Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytz" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/46/dd499ec9038423421951e4fad73051febaa13d2df82b4064f87af8b8c0c3/pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a", size = 320861, upload-time = "2026-05-04T01:35:29.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sounddevice" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/f9/2592608737553638fca98e21e54bfec40bf577bb98a61b2770c912aab25e/sounddevice-0.5.5.tar.gz", hash = "sha256:22487b65198cb5bf2208755105b524f78ad173e5ab6b445bdab1c989f6698df3", size = 143191, upload-time = "2026-01-23T18:36:43.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/0a/478e441fd049002cf308520c0d62dd8333e7c6cc8d997f0dda07b9fbcc46/sounddevice-0.5.5-py3-none-any.whl", hash = "sha256:30ff99f6c107f49d25ad16a45cacd8d91c25a1bcdd3e81a206b921a3a6405b1f", size = 32807, upload-time = "2026-01-23T18:36:35.649Z" }, + { url = "https://files.pythonhosted.org/packages/56/f9/c037c35f6d0b6bc3bc7bfb314f1d6f1f9a341328ef47cd63fc4f850a7b27/sounddevice-0.5.5-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:05eb9fd6c54c38d67741441c19164c0dae8ce80453af2d8c4ad2e7823d15b722", size = 108557, upload-time = "2026-01-23T18:36:37.41Z" }, + { url = "https://files.pythonhosted.org/packages/88/a1/d19dd9889cd4bce2e233c4fac007cd8daaf5b9fe6e6a5d432cf17be0b807/sounddevice-0.5.5-py3-none-win32.whl", hash = "sha256:1234cc9b4c9df97b6cbe748146ae0ec64dd7d6e44739e8e42eaa5b595313a103", size = 317765, upload-time = "2026-01-23T18:36:39.047Z" }, + { url = "https://files.pythonhosted.org/packages/c3/0e/002ed7c4c1c2ab69031f78989d3b789fee3a7fba9e586eb2b81688bf4961/sounddevice-0.5.5-py3-none-win_amd64.whl", hash = "sha256:cfc6b2c49fb7f555591c78cb8ecf48d6a637fd5b6e1db5fec6ed9365d64b3519", size = 365324, upload-time = "2026-01-23T18:36:40.496Z" }, + { url = "https://files.pythonhosted.org/packages/4e/39/a61d4b83a7746b70d23d9173be688c0c6bfc7173772344b7442c2c155497/sounddevice-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3861901ddd8230d2e0e8ae62ac320cdd4c688d81df89da036dcb812f757bb3e6", size = 317115, upload-time = "2026-01-23T18:36:42.235Z" }, +] + +[[package]] +name = "tqdm" +version = "4.68.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/d7/0535a28b1f5f24f6612fb3ff1e89fb1a8d160fee0f976e0aa6803862134b/tqdm-4.68.3.tar.gz", hash = "sha256:00dfa48452b6b6cfae3dd9885636c23d3422d1ec97c66d96818cbd5e0821d482", size = 170596, upload-time = "2026-06-17T07:36:52.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/8e/bb97bb0c71802080bfc8952937d174e49cfc50de5c951dd47b2496f0dcdb/tqdm-4.68.3-py3-none-any.whl", hash = "sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03", size = 78337, upload-time = "2026-06-17T07:36:50.132Z" }, +] + +[[package]] +name = "types-pytz" +version = "2025.2.0.20251108" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/40/ff/c047ddc68c803b46470a357454ef76f4acd8c1088f5cc4891cdd909bfcf6/types_pytz-2025.2.0.20251108.tar.gz", hash = "sha256:fca87917836ae843f07129567b74c1929f1870610681b4c92cb86a3df5817bdb", size = 10961, upload-time = "2025-11-08T02:55:57.001Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/c1/56ef16bf5dcd255155cc736d276efa6ae0a5c26fd685e28f0412a4013c01/types_pytz-2025.2.0.20251108-py3-none-any.whl", hash = "sha256:0f1c9792cab4eb0e46c52f8845c8f77cf1e313cb3d68bf826aa867fe4717d91c", size = 10116, upload-time = "2025-11-08T02:55:56.194Z" }, +] + +[[package]] +name = "types-pytz" +version = "2026.2.0.20260518" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/d9/9fa4019d2235bd374293e1fd4153879b28b6ae1d2bae98addd352c9713f2/types_pytz-2026.2.0.20260518.tar.gz", hash = "sha256:e5d254329e9c4e91f0781b22c43a4bb2d10bb044d97b24c4b05d45567b0eae16", size = 10871, upload-time = "2026-05-18T06:02:45.789Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/89/41e80670779a223d8bc8bc83019a619988cfa5c432cedac5cec23884fbc4/types_pytz-2026.2.0.20260518-py3-none-any.whl", hash = "sha256:3a12eaa38f476bd650902a9c9bb442f03f3c7dee2be5c5848bce61bd708d205a", size = 10125, upload-time = "2026-05-18T06:02:44.968Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, + { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, + { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, + { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, + { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, + { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, + { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, + { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, + { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, + { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/36/db/3fff0bcbe339a6fa6a3b9e3fbc2bfb321ec2f4cd233692272c5a8d6cf801/websockets-15.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5", size = 175424, upload-time = "2025-03-05T20:02:56.505Z" }, + { url = "https://files.pythonhosted.org/packages/46/e6/519054c2f477def4165b0ec060ad664ed174e140b0d1cbb9fafa4a54f6db/websockets-15.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a", size = 173077, upload-time = "2025-03-05T20:02:58.37Z" }, + { url = "https://files.pythonhosted.org/packages/1a/21/c0712e382df64c93a0d16449ecbf87b647163485ca1cc3f6cbadb36d2b03/websockets-15.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b", size = 173324, upload-time = "2025-03-05T20:02:59.773Z" }, + { url = "https://files.pythonhosted.org/packages/1c/cb/51ba82e59b3a664df54beed8ad95517c1b4dc1a913730e7a7db778f21291/websockets-15.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770", size = 182094, upload-time = "2025-03-05T20:03:01.827Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0f/bf3788c03fec679bcdaef787518dbe60d12fe5615a544a6d4cf82f045193/websockets-15.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb", size = 181094, upload-time = "2025-03-05T20:03:03.123Z" }, + { url = "https://files.pythonhosted.org/packages/5e/da/9fb8c21edbc719b66763a571afbaf206cb6d3736d28255a46fc2fe20f902/websockets-15.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054", size = 181397, upload-time = "2025-03-05T20:03:04.443Z" }, + { url = "https://files.pythonhosted.org/packages/2e/65/65f379525a2719e91d9d90c38fe8b8bc62bd3c702ac651b7278609b696c4/websockets-15.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee", size = 181794, upload-time = "2025-03-05T20:03:06.708Z" }, + { url = "https://files.pythonhosted.org/packages/d9/26/31ac2d08f8e9304d81a1a7ed2851c0300f636019a57cbaa91342015c72cc/websockets-15.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed", size = 181194, upload-time = "2025-03-05T20:03:08.844Z" }, + { url = "https://files.pythonhosted.org/packages/98/72/1090de20d6c91994cd4b357c3f75a4f25ee231b63e03adea89671cc12a3f/websockets-15.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880", size = 181164, upload-time = "2025-03-05T20:03:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/2d/37/098f2e1c103ae8ed79b0e77f08d83b0ec0b241cf4b7f2f10edd0126472e1/websockets-15.0.1-cp39-cp39-win32.whl", hash = "sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411", size = 176381, upload-time = "2025-03-05T20:03:12.77Z" }, + { url = "https://files.pythonhosted.org/packages/75/8b/a32978a3ab42cebb2ebdd5b05df0696a09f4d436ce69def11893afa301f0/websockets-15.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4", size = 176841, upload-time = "2025-03-05T20:03:14.367Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, + { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, + { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, + { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, + { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, + { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/b7/48/4b67623bac4d79beb3a6bb27b803ba75c1bdedc06bd827e465803690a4b2/websockets-15.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940", size = 173106, upload-time = "2025-03-05T20:03:29.404Z" }, + { url = "https://files.pythonhosted.org/packages/ed/f0/adb07514a49fe5728192764e04295be78859e4a537ab8fcc518a3dbb3281/websockets-15.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e", size = 173339, upload-time = "2025-03-05T20:03:30.755Z" }, + { url = "https://files.pythonhosted.org/packages/87/28/bd23c6344b18fb43df40d0700f6d3fffcd7cef14a6995b4f976978b52e62/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9", size = 174597, upload-time = "2025-03-05T20:03:32.247Z" }, + { url = "https://files.pythonhosted.org/packages/6d/79/ca288495863d0f23a60f546f0905ae8f3ed467ad87f8b6aceb65f4c013e4/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b", size = 174205, upload-time = "2025-03-05T20:03:33.731Z" }, + { url = "https://files.pythonhosted.org/packages/04/e4/120ff3180b0872b1fe6637f6f995bcb009fb5c87d597c1fc21456f50c848/websockets-15.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f", size = 174150, upload-time = "2025-03-05T20:03:35.757Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c3/30e2f9c539b8da8b1d76f64012f3b19253271a63413b2d3adb94b143407f/websockets-15.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123", size = 176877, upload-time = "2025-03-05T20:03:37.199Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "idna", marker = "python_full_version < '3.10'" }, + { name = "multidict", marker = "python_full_version < '3.10'" }, + { name = "propcache", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, + { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, + { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, + { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, + { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, + { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "multidict", marker = "python_full_version >= '3.10'" }, + { name = "propcache", version = "0.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/df/f1c7a3de0831cd83194f1a85c5bb431b13f81e6b45079314c86d1c4ef3f2/yarl-1.24.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5249a113065c2b7a958bc699759e359cd61cfc81e3069662208f48f191b7ed12", size = 129057, upload-time = "2026-05-19T21:27:47.564Z" }, + { url = "https://files.pythonhosted.org/packages/48/41/7daafb32dd7562bf45b1ce56562e7e1a9146f6479b6456873eb8a3413c40/yarl-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4425fa244fbf530b006d0c5f79ce920114cfff5b4f5f6056e669f8e160fdc0", size = 91545, upload-time = "2026-05-19T21:27:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/a8/8f/7b3ec212f1ea0683f55f978e3246bc313c38818664edfc97a9f349a4901e/yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15c0b5e49d3c44e2a0b93e6a49476c5edad0a7686b92c395765a7ea775572a75", size = 91380, upload-time = "2026-05-19T21:27:51.953Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1b/8bafab7db23b0567ae9db749099b329d91e3b82bc6028b2050ba583e116c/yarl-1.24.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:246d32a53a947c8f0189f5d699cbd4c7036de45d9359e13ba238d1239678c727", size = 105957, upload-time = "2026-05-19T21:27:53.98Z" }, + { url = "https://files.pythonhosted.org/packages/7f/77/21030c2f8d21d21559719beafc772ada2014be933418ed1eaed9cc800e42/yarl-1.24.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:64480fb3e4d4ed9ed71c48a91a477384fc342a50ca30071d2f8a88d51d9c9413", size = 97242, upload-time = "2026-05-19T21:27:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/50/d8/f9ea63d1b6aa910a866e089d871fff6cbd49caab29b86b35221a62dfa0d5/yarl-1.24.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:349de4701dc3760b6e876628423a8f147ef4f5599d10aba1e10702075d424ed9", size = 114719, upload-time = "2026-05-19T21:27:58.037Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a3/04e0ee98ac58a249ea7ed75223f5f901ba81a834f0b4921b58e5cec11757/yarl-1.24.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d162677af8d5d3d6ebab8394b021f4d041ac107a4b705873148a77a49dc9e1b2", size = 112140, upload-time = "2026-05-19T21:27:59.618Z" }, + { url = "https://files.pythonhosted.org/packages/02/ad/0b9cc9f38a7324a7eb1d80f834eaa5283d17e9271bbda3186e598dddaeac/yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5f5c6ec23a9043f2d139cc072f53dd23168d202a334b9b2fda8de4c3e890d90", size = 106721, upload-time = "2026-05-19T21:28:02.586Z" }, + { url = "https://files.pythonhosted.org/packages/65/e7/a52478ebfc66ec989e085c6ae038b9f1bfa4190baa193b133b669c709e2f/yarl-1.24.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:60de6742447fbbf697f16f070b8a443f1b5fe6ca3826fbef9fe70ecd5328e643", size = 106478, upload-time = "2026-05-19T21:28:04.523Z" }, + { url = "https://files.pythonhosted.org/packages/04/d8/5508530fea8472542de00013ae280765fc938ee196fc4030c43a498afb36/yarl-1.24.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acf93187c3710e422368eb768aee98db551ec7c85adc250207a95c16548ab7ac", size = 105423, upload-time = "2026-05-19T21:28:06.515Z" }, + { url = "https://files.pythonhosted.org/packages/84/f1/ece28505e9628e8b756e11bb4f28864a17cc33b6b44db4d2aaf0622bf630/yarl-1.24.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f4b0352fd41fd34b6651934606268816afd6914d09626f9bcbbf018edb0afb3f", size = 99878, upload-time = "2026-05-19T21:28:08.637Z" }, + { url = "https://files.pythonhosted.org/packages/3f/52/fb5d34529b46dd84013afcfb30b8d2bc2832ed03d412736f577d604fa393/yarl-1.24.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:6b208bb939099b4b297438da4e9b25357f0b1c791888669b963e45b203ea9f36", size = 114025, upload-time = "2026-05-19T21:28:10.64Z" }, + { url = "https://files.pythonhosted.org/packages/43/f0/ff9d31aaab024f7a251c0ed308a98ae29bf9f7dc344e78f28b1322431ca2/yarl-1.24.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4b85b8825e631295ff4bc8943f7471d54c533a9360bbe15ebb38e018b555bb8a", size = 105613, upload-time = "2026-05-19T21:28:12.784Z" }, + { url = "https://files.pythonhosted.org/packages/31/7d/3296fb3f3ecd52bf9ae6c16b0895c1cda7e9170a2083861552b683f70264/yarl-1.24.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e26acf20c26cb4fefc631fdb75aca2a6b8fa8b7b5d7f204fb6a8f1e63c706f53", size = 111665, upload-time = "2026-05-19T21:28:14.393Z" }, + { url = "https://files.pythonhosted.org/packages/1a/74/77aa6ddaca4fbf42e45e675a465c43956dd40702281049975a2aa04eae59/yarl-1.24.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:819ca24f8eafcfb683c1bd5f44f2f488cea1274eb8944731ffd2e1f10f619342", size = 106914, upload-time = "2026-05-19T21:28:15.893Z" }, + { url = "https://files.pythonhosted.org/packages/d8/02/7611f22cd1d4ed7373eb7f9ee21fde1046edba2e7c0e514880d760352f48/yarl-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:5cb0f995a901c36be096ccbf4c673591c2faabbe96279598ffaec8c030f85bf4", size = 92658, upload-time = "2026-05-19T21:28:17.471Z" }, + { url = "https://files.pythonhosted.org/packages/91/00/671d0add79938127292839ae44506ce2f7fe8909c72d5a931864f128fd0b/yarl-1.24.2-cp310-cp310-win_arm64.whl", hash = "sha256:f408eace7e22a68b467a0562e0d27d322f91fe3eaaa6f466b962c6cfaea9fa39", size = 87887, upload-time = "2026-05-19T21:28:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c5/1ce244152ff2839645e7cae92f90e7bafcb2c52bea7ff586ac714f14f5df/yarl-1.24.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:36348bebb147b83818b9d7e673ea4debc75970afc6ffdc7e3975ad05ce5a58c1", size = 128971, upload-time = "2026-05-19T21:28:20.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/5a/00f36967203ed89cb3acd2c8ed526cc3fed9418eb70ce128160a911c8499/yarl-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a97e42c8a2233f2f279ecadd9e4a037bcb5d813b78435e8eedd4db5a9e9708c", size = 91507, upload-time = "2026-05-19T21:28:22.556Z" }, + { url = "https://files.pythonhosted.org/packages/31/d0/1fb0c1cd27288f39f6974da4318c32768d72c9890984541fdf1e2e32a51d/yarl-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d027d56f1035e339d1001ac33eceab5b2ec8e42e449787bb75e289fb9a5cd1d", size = 91343, upload-time = "2026-05-19T21:28:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/03/ce/d4a646508bed2f8dec6435b40166fe9308dd191262033d3f307b2bbcaecd/yarl-1.24.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a6377060e7927187a42b7eb202090cbe2b34933a4eeaf90e3bd9e33432e5cae", size = 105704, upload-time = "2026-05-19T21:28:25.872Z" }, + { url = "https://files.pythonhosted.org/packages/4b/07/b3278e82d8bc41485bcf6d856cd0433262593de615b1d3dc43bd3f5bead4/yarl-1.24.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:17076578bce0049a5ce57d14ad1bded391b68a3b213e9b81b0097b090244999a", size = 97281, upload-time = "2026-05-19T21:28:27.352Z" }, + { url = "https://files.pythonhosted.org/packages/17/5b/4cee6e7c92e487bebe7afc797da0aa54a248ab4e776a68fe369ec29665a5/yarl-1.24.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:50713f1d4d6be6375bb178bb43d140ee1acb8abe589cd723320b7925a275be1e", size = 114020, upload-time = "2026-05-19T21:28:29.458Z" }, + { url = "https://files.pythonhosted.org/packages/5c/82/111076571545a7d4f9cca3fbd5c6f40615af58642be09f12328f48022468/yarl-1.24.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:34263e2fa8fb5bb63a0d97706cda38edbad62fddb58c7f12d6acbc092812aa50", size = 111450, upload-time = "2026-05-19T21:28:31.262Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49016d82f032b1bd1e10b01078a7d29ae71bf468eeae0ea22df8bab691e60003", size = 106384, upload-time = "2026-05-19T21:28:32.856Z" }, + { url = "https://files.pythonhosted.org/packages/e5/86/ce41e7a7a199340b2330d52b60f25c4074b6636dd0e60b1a80d31a9db042/yarl-1.24.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3f6d2c216318f8f32038ca3f72501ba08536f0fd18a36e858836b121b2deed9f", size = 106153, upload-time = "2026-05-19T21:28:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5d/31be8a729531ab3e55ac3e7e5c800be8c89ea98947f418b2f6ea259fb6ee/yarl-1.24.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:08d3a33218e0c64393e7610284e770409a9c31c429b078bcb24096ed0a783b8f", size = 105322, upload-time = "2026-05-19T21:28:36.642Z" }, + { url = "https://files.pythonhosted.org/packages/47/9b/b57afb22b386ae87ac9940f09878b98d8c333f89113e6fc96fcf4ca9eb64/yarl-1.24.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5d699376c4ca3cba49bbfae3a05b5b70ded572937171ce1e0b8d87118e2ba294", size = 99057, upload-time = "2026-05-19T21:28:38.386Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4f/06348c27c8389256c313e8a57d796808fc0264c915dd5e7cfd3c0e314dc7/yarl-1.24.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a1cab588b4fa14bea2e55ebea27478adfb05372f47573738e1acc4a36c0b05d2", size = 113502, upload-time = "2026-05-19T21:28:40.091Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1c/284f307b298e4a17b7943b07d9d7ecc4151537f8d137ba51f3bb6c31ca20/yarl-1.24.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ec87ccc31bd21db7ad009d8572c127c1000f268517618a4cc09adba3c2a7f21c", size = 105253, upload-time = "2026-05-19T21:28:41.987Z" }, + { url = "https://files.pythonhosted.org/packages/c8/bf/0de123bec8619e45c80cbded9085f61b5b4a9eddb8abe6d25d28ee1ec866/yarl-1.24.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d1dd47a22843b212baa8d74f37796815d43bd046b42a0f41e9da433386c3136b", size = 111345, upload-time = "2026-05-19T21:28:43.93Z" }, + { url = "https://files.pythonhosted.org/packages/90/af/0248eb065e51129d2a9b2436cd1b5c772c19a6b04e5b6a186955671e3319/yarl-1.24.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7b54b9c67c2b06bd7b9a77253d242124b9c95d2c02def5a1144001ee547dd9d5", size = 106558, upload-time = "2026-05-19T21:28:45.806Z" }, + { url = "https://files.pythonhosted.org/packages/21/3c/f960d7a65ef97d8ba9b424fb5128796a4bc710fc6df2ddbbd7dfdc3bbd20/yarl-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:f8fdbcff8b2c7c9284e60c196f693588598ddcee31e11c18e14949ce44519d45", size = 92808, upload-time = "2026-05-19T21:28:48.465Z" }, + { url = "https://files.pythonhosted.org/packages/03/1a/49fb03750e4de4d2284cd5b885a383133c34eef45bd59631b2bb8b7e81e8/yarl-1.24.2-cp311-cp311-win_arm64.whl", hash = "sha256:b32c37a7a337e90822c45797bf3d79d60875cfcccd3ecc80e9f453d87026c122", size = 87610, upload-time = "2026-05-19T21:28:50.07Z" }, + { url = "https://files.pythonhosted.org/packages/f0/da/866bcb01076ba49d2b42b309867bed3826421f1c479655eb7a607b44f20b/yarl-1.24.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b975866c184564c827e0877380f0dae57dcca7e52782128381b72feff6dfceb8", size = 129957, upload-time = "2026-05-19T21:28:51.695Z" }, + { url = "https://files.pythonhosted.org/packages/bf/1d/fcefb70922ea2268a8971d8e5874d9a8218644200fb8465f1dcad55e6851/yarl-1.24.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3b075301a2836a0e297b1b658cb6d6135df535d62efefdd60366bd589c2c82f2", size = 92164, upload-time = "2026-05-19T21:28:53.242Z" }, + { url = "https://files.pythonhosted.org/packages/29/b6/170e2b8d4e3bc30e6bfdcca53556537f5bf595e938632dfcb059311f3ff6/yarl-1.24.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ae44649b00947634ab0dab2a374a638f52923a6e67083f2c156cd5cbd1a881d", size = 91688, upload-time = "2026-05-19T21:28:54.865Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a5/c9f655d5553ea0b99fdac9d6a99ad3f9b3e73b8e5758bb46f58c9831f74c/yarl-1.24.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507cc19f0b45454e2d6dcd62ff7d062b9f77a2812404e62dbdaec05b50faa035", size = 102902, upload-time = "2026-05-19T21:28:56.963Z" }, + { url = "https://files.pythonhosted.org/packages/5d/bc/6b9664d815d79af4ee553337f9d606c56bbf269186ada9172de45f1b5f60/yarl-1.24.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4c17bad5a530912d2111825d3f05e89bab2dd376aaa8cbc77e449e6db63e576", size = 97931, upload-time = "2026-05-19T21:28:58.56Z" }, + { url = "https://files.pythonhosted.org/packages/98/ec/32ba48acae30fecd60928f5791188b80a9d6ee3840507ffda29fecd37b71/yarl-1.24.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f5f0cbb112838a4a293985b6ed73948a547dadcc1ba6d2089938e7abdedceef8", size = 111030, upload-time = "2026-05-19T21:29:00.148Z" }, + { url = "https://files.pythonhosted.org/packages/82/5a/6f4cd081e5f4934d2ae3a8ef4abe3afacc010d26f0035ee91b35cd7d7c37/yarl-1.24.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ec8356b8a6afcf81fc7aeeef13b1ff7a49dec00f313394bbb9e83830d32ccd7", size = 110392, upload-time = "2026-05-19T21:29:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/323a01c349bd5fb01bb6652e314d9bb218cee630a736bdb810ad50e4013f/yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e7ebcdef69dec6c6451e616f32b622a6d4a2e92b445c992f7c8e5274a6bbc4c", size = 105612, upload-time = "2026-05-19T21:29:04.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/80/264ab684f181e1a876389374519ff05d10248725535ae2ac4e8ac4e563d6/yarl-1.24.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:47a55d6cf6db2f401017a9e96e5288844e5051911fb4e0c8311a3980f5e59a7d", size = 104487, upload-time = "2026-05-19T21:29:06.491Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/efabe5df87e96d7ad5959760b888344be48cd6884db127b407c6b5503adc/yarl-1.24.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3065657c80a2321225e804048597ad55658a7e76b32d6f5ee4074d04c50401db", size = 102333, upload-time = "2026-05-19T21:29:08.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/0c/bcf7c42603e1009295f586d8890f2ba032c8b53310e815adf0a202c73d9f/yarl-1.24.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cb84b80d88e19ede158619b80813968713d8d008b0e2497a576e6a0557d50712", size = 99025, upload-time = "2026-05-19T21:29:10.682Z" }, + { url = "https://files.pythonhosted.org/packages/4f/82/84482ab1a57a0f21a08afe6a7004c61d741f8f2ecc3b05c321577c612164/yarl-1.24.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:990de4f680b1c217e77ff0d6aa0029f9eb79889c11fb3e9a3942c7eba29c1996", size = 110507, upload-time = "2026-05-19T21:29:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8d/a546ba1dfe1b0f290e05fef145cd07614c0f15df1a707195e512d1e39d1d/yarl-1.24.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:abb8ec0323b80161e3802da3150ef660b41d0e9be2048b76a363d93eee992c2b", size = 103719, upload-time = "2026-05-19T21:29:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b6/267f2a09213138473adfce6b8a6e17791d7fee70bd4d9003218e4dec58b0/yarl-1.24.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e7977781f83638a4c73e0f88425563d70173e0dfd90ac006a45c65036293ee3c", size = 110438, upload-time = "2026-05-19T21:29:16.485Z" }, + { url = "https://files.pythonhosted.org/packages/48/2d/1c8d89c7c5f9cad9fb2902445d94e2ab1d7aa35de029afbb8ae95c42d00f/yarl-1.24.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e30dd55825dc554ec5b66a94953b8eda8745926514c5089dfcacecb9c99b5bd1", size = 105719, upload-time = "2026-05-19T21:29:18.367Z" }, + { url = "https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dafe10c12ddd4d120d528c4b5599c953bd7b12845347d507b95451195bb6cad", size = 92901, upload-time = "2026-05-19T21:29:20.014Z" }, + { url = "https://files.pythonhosted.org/packages/39/47/4486ccfb674c04854a1ef8aa77868b6a6f765feaf69633409d7ca4f02cb8/yarl-1.24.2-cp312-cp312-win_arm64.whl", hash = "sha256:044a09d8401fcf8681977faef6d286b8ade1e2d2e9dceda175d1cfa5ca496f30", size = 87229, upload-time = "2026-05-19T21:29:22.1Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978, upload-time = "2026-05-19T21:29:23.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733, upload-time = "2026-05-19T21:29:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113, upload-time = "2026-05-19T21:29:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899, upload-time = "2026-05-19T21:29:28.842Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862, upload-time = "2026-05-19T21:29:31.086Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060, upload-time = "2026-05-19T21:29:32.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613, upload-time = "2026-05-19T21:29:34.379Z" }, + { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012, upload-time = "2026-05-19T21:29:36.216Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887, upload-time = "2026-05-19T21:29:38.746Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620, upload-time = "2026-05-19T21:29:40.368Z" }, + { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599, upload-time = "2026-05-19T21:29:42.66Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604, upload-time = "2026-05-19T21:29:44.632Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161, upload-time = "2026-05-19T21:29:46.755Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619, upload-time = "2026-05-19T21:29:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362, upload-time = "2026-05-19T21:29:51Z" }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667, upload-time = "2026-05-19T21:29:52.743Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069, upload-time = "2026-05-19T21:29:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670, upload-time = "2026-05-19T21:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916, upload-time = "2026-05-19T21:29:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625, upload-time = "2026-05-19T21:30:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574, upload-time = "2026-05-19T21:30:02.544Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534, upload-time = "2026-05-19T21:30:04.319Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481, upload-time = "2026-05-19T21:30:05.988Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529, upload-time = "2026-05-19T21:30:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338, upload-time = "2026-05-19T21:30:09.713Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147, upload-time = "2026-05-19T21:30:11.365Z" }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272, upload-time = "2026-05-19T21:30:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962, upload-time = "2026-05-19T21:30:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063, upload-time = "2026-05-19T21:30:16.683Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438, upload-time = "2026-05-19T21:30:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458, upload-time = "2026-05-19T21:30:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589, upload-time = "2026-05-19T21:30:23.412Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424, upload-time = "2026-05-19T21:30:25.425Z" }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690, upload-time = "2026-05-19T21:30:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248, upload-time = "2026-05-19T21:30:29.297Z" }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084, upload-time = "2026-05-19T21:30:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272, upload-time = "2026-05-19T21:30:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497, upload-time = "2026-05-19T21:30:34.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002, upload-time = "2026-05-19T21:30:37.724Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524, upload-time = "2026-05-19T21:30:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165, upload-time = "2026-05-19T21:30:41.888Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010, upload-time = "2026-05-19T21:30:43.985Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128, upload-time = "2026-05-19T21:30:46.291Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382, upload-time = "2026-05-19T21:30:48.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964, upload-time = "2026-05-19T21:30:49.785Z" }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204, upload-time = "2026-05-19T21:30:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510, upload-time = "2026-05-19T21:30:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584, upload-time = "2026-05-19T21:30:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410, upload-time = "2026-05-19T21:30:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980, upload-time = "2026-05-19T21:30:59.735Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219, upload-time = "2026-05-19T21:31:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, +] From d35627525e192b7a65b556d8a79d22079ccabdf6 Mon Sep 17 00:00:00 2001 From: Hayden Date: Wed, 24 Jun 2026 13:03:09 -0700 Subject: [PATCH 397/408] [codex] Add AWS Bedrock provider authentication (#3398) * [bedrock_sigv4_auth] Add AWS-native Bedrock authentication * Harden AWS authentication for Bedrock * Add Bedrock provider authentication * Cover explicit profile auth precedence * Add opt-in Bedrock live test * Preserve Bedrock profile regions for custom endpoints * Align Bedrock live test with GPT-OSS * Test Bedrock default credential chain * Cover the Bedrock AWS credential chain * Harden Bedrock provider release validation * Make Bedrock auth discoverable to PyInstaller --------- Co-authored-by: Jim Blomo --- .github/workflows/ci.yml | 3 + README.md | 61 +- examples/bedrock.py | 12 +- pyproject.toml | 6 + requirements-dev.lock | 25 +- requirements.lock | 7 + scripts/utils/validate-bedrock-wheel.py | 123 +++ src/openai/__init__.py | 13 + src/openai/_client.py | 266 ++++++- src/openai/_provider.py | 65 ++ src/openai/_utils/_logs.py | 2 +- src/openai/lib/_bedrock_auth.py | 186 +++++ src/openai/lib/azure.py | 9 + src/openai/lib/bedrock.py | 855 +++++++++++++++------ src/openai/providers/__init__.py | 3 + src/openai/providers/bedrock.py | 411 ++++++++++ tests/fixtures/bedrock/v1/sigv4.json | 23 + tests/fixtures/bedrock_auth/v1/cases.json | 292 +++++++ tests/fixtures/bedrock_auth/v1/schema.json | 78 ++ tests/lib/bedrock_live.py | 100 +++ tests/lib/test_bedrock.py | 725 ++++++++++++++++- tests/lib/test_bedrock_auth_conformance.py | 438 +++++++++++ tests/lib/test_bedrock_credential_chain.py | 389 ++++++++++ tests/lib/test_bedrock_provider.py | 436 +++++++++++ tests/test_module_client.py | 53 ++ 25 files changed, 4303 insertions(+), 278 deletions(-) create mode 100644 scripts/utils/validate-bedrock-wheel.py create mode 100644 src/openai/_provider.py create mode 100644 src/openai/lib/_bedrock_auth.py create mode 100644 src/openai/providers/__init__.py create mode 100644 src/openai/providers/bedrock.py create mode 100644 tests/fixtures/bedrock/v1/sigv4.json create mode 100644 tests/fixtures/bedrock_auth/v1/cases.json create mode 100644 tests/fixtures/bedrock_auth/v1/schema.json create mode 100644 tests/lib/bedrock_live.py create mode 100644 tests/lib/test_bedrock_auth_conformance.py create mode 100644 tests/lib/test_bedrock_credential_chain.py create mode 100644 tests/lib/test_bedrock_provider.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9166b6c8b1..91b6ed5e94 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,9 @@ jobs: - name: Run build run: rye build + - name: Validate Bedrock wheel + run: rye run python scripts/utils/validate-bedrock-wheel.py + - name: Get GitHub OIDC Token if: |- github.repository == 'stainless-sdks/openai-python' && diff --git a/README.md b/README.md index 6f87246470..df46c6b690 100644 --- a/README.md +++ b/README.md @@ -928,13 +928,25 @@ An example of using the client with Microsoft Entra ID (formerly known as Azure ## Amazon Bedrock -To use this library with [Amazon Bedrock's OpenAI-compatible API](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html), use the `BedrockOpenAI` class instead of the `OpenAI` class. +To use this library with [Amazon Bedrock's OpenAI-compatible API](https://docs.aws.amazon.com/bedrock/latest/userguide/models-api-compatibility.html), configure the standard `OpenAI` client with the Bedrock provider. + +Install the optional Bedrock dependencies to use the standard AWS credential chain and SigV4 authentication: + +```sh +pip install 'openai[bedrock]' +``` ```py -from openai import BedrockOpenAI +from openai import OpenAI +from openai.providers import bedrock -# gets the bearer token from AWS_BEARER_TOKEN_BEDROCK and the region from AWS_REGION/AWS_DEFAULT_REGION -client = BedrockOpenAI() +# Uses your normal AWS credentials. You can omit region when it is +# configured through AWS_REGION, AWS_DEFAULT_REGION, or your AWS profile. +client = OpenAI( + provider=bedrock( + region="us-west-2", + ) +) response = client.responses.create( model="openai.gpt-5.4", @@ -944,19 +956,52 @@ response = client.responses.create( print(response.output_text) ``` -`BedrockOpenAI` configures AWS bearer auth and the Bedrock Mantle endpoint, then uses the normal SDK resources. AWS controls which endpoints and features are supported; unsupported calls surface the provider's normal HTTP errors through the SDK. +The provider configures AWS authentication and the Bedrock Mantle endpoint while retaining the normal SDK resources, retries, streaming, and error handling. AWS controls which endpoints and features are supported; unsupported calls surface the provider's normal HTTP errors through the SDK. + +The default AWS credential chain supports environment credentials, shared credentials and config files, named profiles, SSO and assume-role profiles, and workload credentials such as ECS, EKS, and EC2 metadata. To select a named profile: + +```py +client = OpenAI( + provider=bedrock( + profile="my-profile", + ) +) +``` + +You can also pass `access_key_id` and `secret_access_key`, with an optional `session_token`, or a refreshable `credential_provider` that returns botocore-compatible credentials. Explicit bearer and AWS credential options are mutually exclusive. -Pass `base_url` or set `AWS_BEDROCK_BASE_URL` to override the derived `https://bedrock-mantle..api.aws/openai/v1` endpoint. The legacy module client supports `openai.api_type = "amazon-bedrock"` or `OPENAI_API_TYPE=amazon-bedrock`. +Pass `base_url` to `bedrock(...)` or set `AWS_BEDROCK_BASE_URL` to override the derived `https://bedrock-mantle..api.aws/openai/v1` endpoint. -Set `AWS_BEARER_TOKEN_BEDROCK` to an [Amazon Bedrock API key](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html). To refresh tokens yourself, pass a provider instead of `api_key`: +SigV4 requests require replayable, fully serialized request bodies. Standard JSON requests already meet this requirement, and response streaming is unaffected. Low-level one-shot request streams must be buffered before sending, or sent with bearer authentication and retries disabled. + +Bearer tokens remain available as a compatibility or manual authentication mode. Set `AWS_BEARER_TOKEN_BEDROCK` to an [Amazon Bedrock API key](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html), pass `api_key`, or provide a refresh callback: ```py +client = OpenAI( + provider=bedrock( + region="us-west-2", + token_provider=lambda: refresh_bedrock_token(), + ) +) +``` + +Without explicit authentication, `AWS_BEARER_TOKEN_BEDROCK` takes precedence over the default AWS credential chain for backwards compatibility. + +### Legacy `BedrockOpenAI` client + +`BedrockOpenAI` and `AsyncBedrockOpenAI` remain available for existing applications and delegate to the same provider implementation. New applications should prefer `OpenAI(provider=bedrock(...))`. + +```py +from openai import BedrockOpenAI + client = BedrockOpenAI( aws_region="us-west-2", - bedrock_token_provider=lambda: refresh_bedrock_token(), + aws_profile="my-profile", ) ``` +The legacy module client also continues to support `openai.api_type = "amazon-bedrock"` or `OPENAI_API_TYPE=amazon-bedrock`. + ## Versioning 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: diff --git a/examples/bedrock.py b/examples/bedrock.py index 24dafb5b80..6a1837ef49 100644 --- a/examples/bedrock.py +++ b/examples/bedrock.py @@ -1,9 +1,11 @@ -from openai import BedrockOpenAI +from openai import OpenAI +from openai.providers import bedrock -client = BedrockOpenAI() - -# For refreshed Bedrock bearer tokens: -# client = BedrockOpenAI(aws_region="us-west-2", bedrock_token_provider=get_bedrock_token) +client = OpenAI( + provider=bedrock( + region="us-west-2", + ) +) response = client.responses.create( model="openai.gpt-5.4", diff --git a/pyproject.toml b/pyproject.toml index 239edef8c8..407dd73b12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,10 @@ aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] realtime = ["websockets >= 13, < 16"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] +bedrock = [ + "botocore>=1.40.0,<1.43; python_version < '3.10'", + "botocore>=1.40.0,<2; python_version >= '3.10'", +] [tool.rye] managed = true @@ -57,6 +61,7 @@ dev-dependencies = [ "respx", "pytest", "pytest-asyncio", + "jsonschema>=4.23.0", "ruff", "time-machine", "nox", @@ -65,6 +70,7 @@ dev-dependencies = [ "rich>=13.7.1", "inline-snapshot>=0.28.0", "azure-identity >=1.14.1", + "botocore==1.42.97", "types-tqdm > 4", "types-pyaudio > 0", "trio >=0.22.2", diff --git a/requirements-dev.lock b/requirements-dev.lock index 73312bcee1..9ddb744eaf 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -30,14 +30,18 @@ async-timeout==5.0.1 # via aiohttp attrs==25.4.0 # via aiohttp + # via jsonschema # via nox # via outcome + # via referencing # via trio azure-core==1.36.0 # via azure-identity azure-identity==1.25.1 backports-asyncio-runner==1.2.0 # via pytest-asyncio +botocore==1.42.97 + # via openai certifi==2026.1.4 # via httpcore # via httpx @@ -100,6 +104,11 @@ iniconfig==2.1.0 inline-snapshot==0.31.1 jiter==0.12.0 # via openai +jmespath==1.1.0 + # via botocore +jsonschema==4.25.1 +jsonschema-specifications==2025.9.1 + # via jsonschema markdown-it-py==3.0.0 # via rich mdurl==0.1.2 @@ -161,16 +170,23 @@ pytest==8.4.2 pytest-asyncio==1.2.0 pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 + # via botocore # via pandas # via time-machine pytz==2025.2 # via pandas +referencing==0.36.2 + # via jsonschema + # via jsonschema-specifications requests==2.32.5 # via azure-core # via msal respx==0.22.0 rich==14.2.0 # via inline-snapshot +rpds-py==0.27.1 + # via jsonschema + # via referencing ruff==0.14.7 six==1.17.0 # via python-dateutil @@ -194,9 +210,11 @@ trio==0.31.0 types-pyaudio==0.2.16.20250801 types-pytz==2025.2.0.20251108 # via pandas-stubs -types-requests==2.32.4.20250913 +types-requests==2.31.0.6 # via types-tqdm types-tqdm==4.67.0.20250809 +types-urllib3==1.26.25.14 + # via types-requests typing-extensions==4.15.0 # via aiosignal # via anyio @@ -211,15 +229,16 @@ typing-extensions==4.15.0 # via pydantic-core # via pyright # via pytest-asyncio + # via referencing # via typing-inspection # via virtualenv typing-inspection==0.4.2 # via pydantic tzdata==2025.2 # via pandas -urllib3==2.5.0 +urllib3==1.26.20 + # via botocore # via requests - # via types-requests virtualenv==20.35.4 # via nox websockets==15.0.1 diff --git a/requirements.lock b/requirements.lock index af6e4f99e8..4a209f1034 100644 --- a/requirements.lock +++ b/requirements.lock @@ -26,6 +26,8 @@ async-timeout==5.0.1 # via aiohttp attrs==25.4.0 # via aiohttp +botocore==1.42.97 + # via openai certifi==2026.1.4 # via httpcore # via httpx @@ -53,6 +55,8 @@ idna==3.11 # via yarl jiter==0.12.0 # via openai +jmespath==1.1.0 + # via botocore multidict==6.7.0 # via aiohttp # via yarl @@ -74,6 +78,7 @@ pydantic==2.12.5 pydantic-core==2.41.5 # via pydantic python-dateutil==2.9.0.post0 + # via botocore # via pandas pytz==2025.2 # via pandas @@ -100,6 +105,8 @@ typing-inspection==0.4.2 # via pydantic tzdata==2025.2 # via pandas +urllib3==1.26.20 + # via botocore websockets==15.0.1 # via openai yarl==1.22.0 diff --git a/scripts/utils/validate-bedrock-wheel.py b/scripts/utils/validate-bedrock-wheel.py new file mode 100644 index 0000000000..29345e1016 --- /dev/null +++ b/scripts/utils/validate-bedrock-wheel.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import os +import sys +import email +import zipfile +import tempfile +import subprocess +from pathlib import Path + +_SMOKE_TEST = r""" +import os +import sys +import importlib.abc +from pathlib import Path + +import httpx + + +class BlockBotocore(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + if fullname == "botocore" or fullname.startswith("botocore."): + raise ImportError(f"unexpected AWS import: {fullname}") + return None + + +wheel_root = Path(os.environ["OPENAI_WHEEL_ROOT"]).resolve() +blocker = BlockBotocore() +sys.meta_path.insert(0, blocker) + +import openai +from openai import OpenAI +from openai.providers import bedrock + +assert Path(openai.__file__).resolve().is_relative_to(wheel_root), openai.__file__ + +requests = [] + + +def handler(request): + requests.append(request) + return httpx.Response(200, request=request, json={}) + + +http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False) +with OpenAI( + provider=bedrock(region="us-east-1", api_key="bearer-token"), + http_client=http_client, +) as client: + client.get("/models", cast_to=httpx.Response) + +assert requests[0].headers["Authorization"] == "Bearer bearer-token" +assert not any(name == "botocore" or name.startswith("botocore.") for name in sys.modules) + +sys.meta_path.remove(blocker) +requests.clear() +http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False) +with OpenAI( + provider=bedrock( + region="us-east-1", + access_key_id="fixture-access-key", + secret_access_key="fixture-secret-key", + session_token="fixture-session-token", + ), + http_client=http_client, +) as client: + client.get("/models", cast_to=httpx.Response) + +assert "Credential=fixture-access-key/" in requests[0].headers["Authorization"] +assert requests[0].headers["X-Amz-Security-Token"] == "fixture-session-token" +""" + + +def main() -> None: + wheels = list(Path("dist").glob("*.whl")) + if len(wheels) != 1: + raise RuntimeError(f"Expected exactly one wheel in dist/, found: {wheels}") + + wheel = wheels[0] + with tempfile.TemporaryDirectory() as directory: + wheel_root = Path(directory) + with zipfile.ZipFile(wheel) as archive: + metadata_names = [name for name in archive.namelist() if name.endswith(".dist-info/METADATA")] + if len(metadata_names) != 1: + raise RuntimeError(f"Expected exactly one METADATA file in {wheel}, found: {metadata_names}") + + metadata = email.message_from_bytes(archive.read(metadata_names[0])) + archive.extractall(wheel_root) + + requirements = metadata.get_all("Requires-Dist", []) + botocore_requirements = [requirement for requirement in requirements if requirement.startswith("botocore")] + if len(botocore_requirements) != 2: + raise RuntimeError(f"Expected two Python-version-specific botocore requirements: {botocore_requirements}") + if any("[crt]" in requirement for requirement in botocore_requirements): + raise RuntimeError( + f"The Bedrock extra must not install the unused botocore CRT extra: {botocore_requirements}" + ) + if not all("extra == 'bedrock'" in requirement for requirement in botocore_requirements): + raise RuntimeError(f"Botocore requirements must belong to the Bedrock extra: {botocore_requirements}") + + environment = os.environ.copy() + for name in ( + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_SESSION_TOKEN", + "AWS_PROFILE", + "AWS_REGION", + "AWS_DEFAULT_REGION", + "AWS_BEARER_TOKEN_BEDROCK", + "AWS_BEDROCK_BASE_URL", + "OPENAI_API_KEY", + "OPENAI_ORG_ID", + "OPENAI_PROJECT_ID", + "OPENAI_CUSTOM_HEADERS", + ): + environment.pop(name, None) + environment["OPENAI_WHEEL_ROOT"] = str(wheel_root) + environment["PYTHONPATH"] = str(wheel_root) + subprocess.run([sys.executable, "-c", _SMOKE_TEST], cwd=wheel_root, env=environment, check=True) + + +if __name__ == "__main__": + main() diff --git a/src/openai/__init__.py b/src/openai/__init__.py index 3786d106cb..a3f3237c38 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -313,6 +313,19 @@ def api_key(self, value: str | None) -> None: # type: ignore _bedrock_api_key = value + @override + def _refresh_api_key(self) -> str: + if api_key is not None: + return api_key + + return super()._refresh_api_key() + + @override + def _legacy_auth_configuration(self) -> _bedrock._LegacyAuthConfiguration: + if api_key is not None: + return ("bearer", api_key) + return super()._legacy_auth_configuration() + class _AmbiguousModuleClientUsageError(OpenAIError): def __init__(self) -> None: diff --git a/src/openai/_client.py b/src/openai/_client.py index a3ca9a7148..66d03b23dd 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -31,6 +31,7 @@ from ._compat import cached_property from ._models import SecurityOptions, FinalRequestOptions from ._version import __version__ +from ._provider import _Provider, _provider_name, _ProviderRuntime, _configure_provider from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import OpenAIError, APIStatusError from ._base_client import ( @@ -110,6 +111,8 @@ class OpenAI(SyncAPIClient): project: str | None webhook_secret: str | None _workload_identity_auth: WorkloadIdentityAuth | None + _provider: _Provider | None + _provider_runtime: _ProviderRuntime | None websocket_base_url: str | httpx.URL | None """Base URL for WebSocket connections. @@ -128,6 +131,7 @@ def __init__( organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, + provider: _Provider | None = None, base_url: str | httpx.URL | None = None, websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, @@ -157,13 +161,44 @@ def __init__( - `organization` from `OPENAI_ORG_ID` - `project` from `OPENAI_PROJECT_ID` - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` + + When `provider` is supplied, authentication and the base URL are configured by that provider instead. """ + provider_runtime: _ProviderRuntime | None = None + if provider is not None: + provider_name = _provider_name(provider) + conflicts = [ + name + for name, value in ( + ("api_key", api_key), + ("admin_api_key", admin_api_key), + ("workload_identity", workload_identity), + ("base_url", base_url), + ) + if value is not None + ] + if conflicts: + formatted = ", ".join(f"`{name}`" for name in conflicts) + raise OpenAIError( + f"`provider` cannot be combined with top-level {formatted}. " + f"Move provider authentication and routing options into `{provider_name}(...)`." + ) + + provider_runtime = _configure_provider(provider) + + self._provider = provider + self._provider_runtime = provider_runtime + if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None: raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive") - self.workload_identity = workload_identity + self.workload_identity = workload_identity if provider_runtime is None else None - if workload_identity is not None: + if provider_runtime is not None: + self.api_key = "" + self._api_key_provider = None + self._workload_identity_auth = None + elif workload_identity is not None: self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER self._api_key_provider = None self._workload_identity_auth = WorkloadIdentityAuth( @@ -180,12 +215,13 @@ def __init__( self._api_key_provider = None self._workload_identity_auth = None - if admin_api_key is None: + if admin_api_key is None and provider_runtime is None: admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") - self.admin_api_key = admin_api_key + self.admin_api_key = admin_api_key if provider_runtime is None else None if ( - _enforce_credentials + provider_runtime is None + and _enforce_credentials and not self.api_key and self._api_key_provider is None and workload_identity is None @@ -195,11 +231,11 @@ def __init__( "Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable." ) - if organization is None: + if organization is None and provider_runtime is None: organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization - if project is None: + if project is None and provider_runtime is None: project = os.environ.get("OPENAI_PROJECT_ID") self.project = project @@ -209,12 +245,14 @@ def __init__( self.websocket_base_url = websocket_base_url - if base_url is None: + if provider_runtime is not None: + base_url = provider_runtime.base_url + elif base_url is None: base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: base_url = f"https://api.openai.com/v1" - custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") + custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None if custom_headers_env is not None: parsed: dict[str, str] = {} for line in custom_headers_env.split("\n"): @@ -437,10 +475,16 @@ def _send_request( stream: bool, **kwargs: Unpack[HttpxSendArgs], ) -> httpx.Response: - return self._send_with_auth_retry(request, stream=stream, **kwargs) + response = self._send_with_auth_retry(request, stream=stream, **kwargs) + if self._provider_runtime is not None and self._provider_runtime.normalize_response is not None: + response = self._provider_runtime.normalize_response(response) + return response @override def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + headers: dict[str, str] = {} if security.get("bearer_auth", False): for key, value in self._bearer_auth.items(): @@ -460,6 +504,9 @@ def _bearer_auth(self) -> dict[str, str]: @property @override def auth_headers(self) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + api_key = self.api_key if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: return {} @@ -485,6 +532,9 @@ def default_headers(self) -> dict[str, str | Omit]: @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self._provider_runtime is not None: + return + if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return @@ -494,11 +544,26 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: @override def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - if self._api_key_provider is not None and options.security.get("bearer_auth", False): + if self._provider_runtime is not None: + if self._provider_runtime.transform_request is not None: + options = self._provider_runtime.transform_request(options) + elif self._api_key_provider is not None and options.security.get("bearer_auth", False): self._refresh_api_key() return super()._prepare_options(options) + @override + def _prepare_request(self, request: httpx.Request) -> None: + if self._provider_runtime is not None and self._provider_runtime.prepare_request is not None: + self._provider_runtime.prepare_request(request) + + @override + def _custom_auth(self, security: SecurityOptions) -> httpx.Auth | None: + if self._provider_runtime is not None: + return httpx.Auth() + + return super()._custom_auth(security) + def _refresh_api_key(self) -> str: if self._api_key_provider is not None: self.api_key = self._api_key_provider() @@ -511,6 +576,7 @@ def copy( api_key: str | Callable[[], str] | None = None, admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = not_given, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -535,7 +601,11 @@ def copy( if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - headers = self._custom_headers + provider_changed = not isinstance(provider, NotGiven) and provider is not self._provider + inherited_organization = None if provider_changed else self.organization + inherited_project = None if provider_changed else self.project + + headers: Mapping[str, str] = {} if provider_changed else self._custom_headers if default_headers is not None: headers = {**headers, **default_headers} elif set_default_headers is not None: @@ -549,21 +619,43 @@ def copy( http_client = http_client or self._client + next_provider = self._provider if isinstance(provider, NotGiven) else provider + auth_options: dict[str, Any] + if next_provider is not None: + auth_options = { + "provider": next_provider, + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + elif self._provider is not None: + auth_options = { + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + else: + auth_options = { + "api_key": api_key or self._api_key_provider or self.api_key, + "admin_api_key": admin_api_key or self.admin_api_key, + "workload_identity": workload_identity or self.workload_identity, + "base_url": base_url or self.base_url, + } + return self.__class__( - api_key=api_key or self._api_key_provider or self.api_key, - admin_api_key=admin_api_key or self.admin_api_key, - workload_identity=workload_identity or self.workload_identity, - organization=organization or self.organization, - project=project or self.project, + organization=organization or inherited_organization, + project=project or inherited_project, webhook_secret=webhook_secret or self.webhook_secret, websocket_base_url=websocket_base_url or self.websocket_base_url, - base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, + **auth_options, **_extra_kwargs, ) @@ -615,6 +707,8 @@ class AsyncOpenAI(AsyncAPIClient): project: str | None webhook_secret: str | None _workload_identity_auth: WorkloadIdentityAuth | None + _provider: _Provider | None + _provider_runtime: _ProviderRuntime | None websocket_base_url: str | httpx.URL | None """Base URL for WebSocket connections. @@ -633,6 +727,7 @@ def __init__( organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, + provider: _Provider | None = None, base_url: str | httpx.URL | None = None, websocket_base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, @@ -662,13 +757,44 @@ def __init__( - `organization` from `OPENAI_ORG_ID` - `project` from `OPENAI_PROJECT_ID` - `webhook_secret` from `OPENAI_WEBHOOK_SECRET` + + When `provider` is supplied, authentication and the base URL are configured by that provider instead. """ + provider_runtime: _ProviderRuntime | None = None + if provider is not None: + provider_name = _provider_name(provider) + conflicts = [ + name + for name, value in ( + ("api_key", api_key), + ("admin_api_key", admin_api_key), + ("workload_identity", workload_identity), + ("base_url", base_url), + ) + if value is not None + ] + if conflicts: + formatted = ", ".join(f"`{name}`" for name in conflicts) + raise OpenAIError( + f"`provider` cannot be combined with top-level {formatted}. " + f"Move provider authentication and routing options into `{provider_name}(...)`." + ) + + provider_runtime = _configure_provider(provider) + + self._provider = provider + self._provider_runtime = provider_runtime + if api_key is not None and api_key != WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER and workload_identity is not None: raise OpenAIError("The `api_key` and `workload_identity` arguments are mutually exclusive") - self.workload_identity = workload_identity + self.workload_identity = workload_identity if provider_runtime is None else None - if workload_identity is not None: + if provider_runtime is not None: + self.api_key = "" + self._api_key_provider = None + self._workload_identity_auth = None + elif workload_identity is not None: self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER self._api_key_provider = None self._workload_identity_auth = WorkloadIdentityAuth( @@ -685,12 +811,13 @@ def __init__( self._api_key_provider = None self._workload_identity_auth = None - if admin_api_key is None: + if admin_api_key is None and provider_runtime is None: admin_api_key = os.environ.get("OPENAI_ADMIN_KEY") - self.admin_api_key = admin_api_key + self.admin_api_key = admin_api_key if provider_runtime is None else None if ( - _enforce_credentials + provider_runtime is None + and _enforce_credentials and not self.api_key and self._api_key_provider is None and workload_identity is None @@ -700,11 +827,11 @@ def __init__( "Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable." ) - if organization is None: + if organization is None and provider_runtime is None: organization = os.environ.get("OPENAI_ORG_ID") self.organization = organization - if project is None: + if project is None and provider_runtime is None: project = os.environ.get("OPENAI_PROJECT_ID") self.project = project @@ -714,12 +841,14 @@ def __init__( self.websocket_base_url = websocket_base_url - if base_url is None: + if provider_runtime is not None: + base_url = provider_runtime.base_url + elif base_url is None: base_url = os.environ.get("OPENAI_BASE_URL") if base_url is None: base_url = f"https://api.openai.com/v1" - custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") + custom_headers_env = os.environ.get("OPENAI_CUSTOM_HEADERS") if provider_runtime is None else None if custom_headers_env is not None: parsed: dict[str, str] = {} for line in custom_headers_env.split("\n"): @@ -942,10 +1071,19 @@ async def _send_request( stream: bool, **kwargs: Unpack[HttpxSendArgs], ) -> httpx.Response: - return await self._send_with_auth_retry(request, stream=stream, **kwargs) + response = await self._send_with_auth_retry(request, stream=stream, **kwargs) + if self._provider_runtime is not None: + if self._provider_runtime.normalize_async_response is not None: + response = await self._provider_runtime.normalize_async_response(response) + elif self._provider_runtime.normalize_response is not None: + response = self._provider_runtime.normalize_response(response) + return response @override def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + headers: dict[str, str] = {} if security.get("bearer_auth", False): for key, value in self._bearer_auth.items(): @@ -965,6 +1103,9 @@ def _bearer_auth(self) -> dict[str, str]: @property @override def auth_headers(self) -> dict[str, str]: + if self._provider_runtime is not None: + return {} + api_key = self.api_key if not api_key or api_key == WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER: return {} @@ -990,6 +1131,9 @@ def default_headers(self) -> dict[str, str | Omit]: @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self._provider_runtime is not None: + return + if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return @@ -999,11 +1143,34 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: @override async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - if self._api_key_provider is not None and options.security.get("bearer_auth", False): + if self._provider_runtime is not None: + if self._provider_runtime.transform_async_request is not None: + options = await self._provider_runtime.transform_async_request(options) + elif self._provider_runtime.transform_request is not None: + options = self._provider_runtime.transform_request(options) + elif self._api_key_provider is not None and options.security.get("bearer_auth", False): await self._refresh_api_key() return await super()._prepare_options(options) + @override + async def _prepare_request(self, request: httpx.Request) -> None: + if self._provider_runtime is None: + return + + if self._provider_runtime.prepare_async_request is not None: + await self._provider_runtime.prepare_async_request(request) + elif self._provider_runtime.prepare_request is not None: + self._provider_runtime.prepare_request(request) + + @property + @override + def custom_auth(self) -> httpx.Auth | None: + if self._provider_runtime is not None: + return httpx.Auth() + + return super().custom_auth + async def _refresh_api_key(self) -> str: if self._api_key_provider is not None: self.api_key = await self._api_key_provider() @@ -1016,6 +1183,7 @@ def copy( api_key: str | Callable[[], Awaitable[str]] | None = None, admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = not_given, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -1040,7 +1208,11 @@ def copy( if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - headers = self._custom_headers + provider_changed = not isinstance(provider, NotGiven) and provider is not self._provider + inherited_organization = None if provider_changed else self.organization + inherited_project = None if provider_changed else self.project + + headers: Mapping[str, str] = {} if provider_changed else self._custom_headers if default_headers is not None: headers = {**headers, **default_headers} elif set_default_headers is not None: @@ -1053,21 +1225,43 @@ def copy( params = set_default_query http_client = http_client or self._client + next_provider = self._provider if isinstance(provider, NotGiven) else provider + auth_options: dict[str, Any] + if next_provider is not None: + auth_options = { + "provider": next_provider, + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + elif self._provider is not None: + auth_options = { + "api_key": api_key, + "admin_api_key": admin_api_key, + "workload_identity": workload_identity, + "base_url": base_url, + } + else: + auth_options = { + "api_key": api_key or self._api_key_provider or self.api_key, + "admin_api_key": admin_api_key or self.admin_api_key, + "workload_identity": workload_identity or self.workload_identity, + "base_url": base_url or self.base_url, + } + return self.__class__( - api_key=api_key or self._api_key_provider or self.api_key, - admin_api_key=admin_api_key or self.admin_api_key, - workload_identity=workload_identity or self.workload_identity, - organization=organization or self.organization, - project=project or self.project, + organization=organization or inherited_organization, + project=project or inherited_project, webhook_secret=webhook_secret or self.webhook_secret, websocket_base_url=websocket_base_url or self.websocket_base_url, - base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, + **auth_options, **_extra_kwargs, ) diff --git a/src/openai/_provider.py b/src/openai/_provider.py new file mode 100644 index 0000000000..2874d11ff3 --- /dev/null +++ b/src/openai/_provider.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from typing import Callable, Protocol, Awaitable +from weakref import WeakKeyDictionary +from dataclasses import dataclass + +import httpx + +from ._models import FinalRequestOptions +from ._exceptions import OpenAIError + + +class _Provider: + """Opaque configuration returned by an OpenAI-owned provider factory.""" + + __slots__ = ("__weakref__",) + + +@dataclass +class _ProviderRuntime: + name: str + base_url: str | httpx.URL + transform_request: Callable[[FinalRequestOptions], FinalRequestOptions] | None = None + transform_async_request: Callable[[FinalRequestOptions], Awaitable[FinalRequestOptions]] | None = None + prepare_request: Callable[[httpx.Request], None] | None = None + prepare_async_request: Callable[[httpx.Request], Awaitable[None]] | None = None + normalize_response: Callable[[httpx.Response], httpx.Response] | None = None + normalize_async_response: Callable[[httpx.Response], Awaitable[httpx.Response]] | None = None + + +class _ProviderDefinition(Protocol): + @property + def name(self) -> str: ... + + def configure(self) -> _ProviderRuntime: ... + + +# Provider factories capture configuration in definitions, while every client +# gets fresh runtime state from ``definition.configure()``. Keeping definitions +# outside the opaque provider object prevents arbitrary objects (including a +# directly constructed ``_Provider``) from imitating an OpenAI-owned provider +# and keeps credentials out of the object's representation. The weak mapping +# also avoids retaining provider configuration after the public handle is gone. +_provider_definitions: WeakKeyDictionary[_Provider, _ProviderDefinition] = WeakKeyDictionary() + + +def _create_provider(definition: _ProviderDefinition) -> _Provider: # pyright: ignore[reportUnusedFunction] + provider = _Provider() + _provider_definitions[provider] = definition + return provider + + +def _provider_name(provider: _Provider) -> str: # pyright: ignore[reportUnusedFunction] + return _get_provider_definition(provider).name + + +def _configure_provider(provider: _Provider) -> _ProviderRuntime: # pyright: ignore[reportUnusedFunction] + return _get_provider_definition(provider).configure() + + +def _get_provider_definition(provider: _Provider) -> _ProviderDefinition: + try: + return _provider_definitions[provider] + except (KeyError, TypeError) as exc: + raise OpenAIError("Invalid provider. Providers must be created by an OpenAI provider factory.") from exc diff --git a/src/openai/_utils/_logs.py b/src/openai/_utils/_logs.py index 376946933c..eaffa5ec7a 100644 --- a/src/openai/_utils/_logs.py +++ b/src/openai/_utils/_logs.py @@ -8,7 +8,7 @@ httpx_logger: logging.Logger = logging.getLogger("httpx") -SENSITIVE_HEADERS = {"api-key", "authorization"} +SENSITIVE_HEADERS = {"api-key", "authorization", "x-amz-security-token"} def _basic_config() -> None: diff --git a/src/openai/lib/_bedrock_auth.py b/src/openai/lib/_bedrock_auth.py new file mode 100644 index 0000000000..2921858195 --- /dev/null +++ b/src/openai/lib/_bedrock_auth.py @@ -0,0 +1,186 @@ +# pyright: reportMissingTypeStubs=false + +from __future__ import annotations + +import os +import hashlib +from typing import Any, Literal, Mapping, Callable, Protocol, cast +from dataclasses import field, dataclass + +from .._exceptions import OpenAIError + +AwsCredentialsProvider = Callable[[], object] + + +class _BotocoreSession(Protocol): + def get_credentials(self) -> object | None: ... + + +_AUTHORIZATION = "authorization" +_AWS_SIGNING_HEADERS = ( + _AUTHORIZATION, + "x-amz-content-sha256", + "x-amz-date", + "x-amz-security-token", +) + + +def _load_botocore() -> tuple[Any, Any, Any, Any]: + try: + from botocore.auth import SigV4Auth # type: ignore[import-untyped] + from botocore.session import Session # type: ignore[import-untyped] + from botocore.awsrequest import AWSRequest # type: ignore[import-untyped] + from botocore.credentials import Credentials # type: ignore[import-untyped] + except ImportError as exc: + raise OpenAIError( + "Bedrock AWS authentication requires optional AWS dependencies. " + "Install them with `pip install openai[bedrock]` and try again." + ) from exc + + return SigV4Auth, AWSRequest, Credentials, Session + + +@dataclass(frozen=True) +class BedrockAwsAuthConfig: + region: str + source: Literal["static", "profile", "provider", "default"] + region_source: Literal["explicit", "environment", "profile"] = "explicit" + profile: str | None = None + access_key_id: str | None = field(default=None, repr=False) + secret_access_key: str | None = field(default=None, repr=False) + session_token: str | None = field(default=None, repr=False) + credentials_provider: AwsCredentialsProvider | None = field(default=None, repr=False, compare=False) + + +class BedrockAwsAuth: + def __init__(self, config: BedrockAwsAuthConfig, *, session: _BotocoreSession | None = None) -> None: + sigv4_auth_cls, aws_request_cls, credentials_cls, session_cls = _load_botocore() + + if session is None: + try: + session = session_cls(profile=config.profile) + except Exception as exc: + raise OpenAIError( + "Failed to resolve AWS credentials for Bedrock. Verify your AWS profile, environment variables, " + "or runtime identity configuration and try again." + ) from exc + + assert session is not None + self.config = config + self._session = session + self._credentials_provider = config.credentials_provider + self._explicit_credentials = ( + credentials_cls(config.access_key_id, config.secret_access_key, config.session_token) + if config.access_key_id is not None and config.secret_access_key is not None + else None + ) + self._aws_request_cls = aws_request_cls + self._sigv4_auth_cls = sigv4_auth_cls + + @classmethod + def resolve( + cls, + *, + region: str | None, + profile: str | None, + access_key_id: str | None, + secret_access_key: str | None, + session_token: str | None, + credentials_provider: AwsCredentialsProvider | None, + ) -> BedrockAwsAuth: + _, _, _, session_cls = _load_botocore() + + try: + session = session_cls(profile=profile) + resolved_region, region_source = resolve_aws_region_with_source(region, session=session) + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError( + "Failed to resolve AWS credentials for Bedrock. Verify your AWS profile, environment variables, " + "or runtime identity configuration and try again." + ) from exc + + source: Literal["static", "profile", "provider", "default"] + if access_key_id is not None: + source = "static" + elif profile is not None: + source = "profile" + elif credentials_provider is not None: + source = "provider" + else: + source = "default" + + config = BedrockAwsAuthConfig( + region=resolved_region, + source=source, + region_source=region_source, + profile=profile, + access_key_id=access_key_id, + secret_access_key=secret_access_key, + session_token=session_token, + credentials_provider=credentials_provider, + ) + return cls(config, session=session) + + def sign(self, *, method: str, url: str, headers: Mapping[str, str], body: bytes | None) -> dict[str, str]: + try: + credentials = ( + self._credentials_provider() + if self._credentials_provider is not None + else self._explicit_credentials or self._session.get_credentials() + ) + if credentials is None: + raise OpenAIError( + "Could not find credentials for Bedrock. Pass a bearer credential or AWS credentials to " + "`bedrock(...)`, " + "set `AWS_BEARER_TOKEN_BEDROCK`, or configure the default AWS credential chain." + ) + + get_frozen_credentials = getattr(credentials, "get_frozen_credentials", None) + if callable(get_frozen_credentials): + credentials = get_frozen_credentials() + + signed_headers = { + name: value for name, value in headers.items() if name.lower() not in _AWS_SIGNING_HEADERS + } + signed_headers["X-Amz-Content-SHA256"] = hashlib.sha256(body or b"").hexdigest() + aws_request = self._aws_request_cls( + method=method, + url=url, + data=body, + headers=signed_headers, + ) + self._sigv4_auth_cls(credentials, "bedrock-mantle", self.config.region).add_auth(aws_request) + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError( + "Failed to resolve AWS credentials for Bedrock. Verify your AWS profile, environment variables, " + "or runtime identity configuration and try again." + ) from exc + + return dict(aws_request.headers.items()) + + +def resolve_aws_region_with_source( + aws_region: str | None, *, session: object | None = None +) -> tuple[str, Literal["explicit", "environment", "profile"]]: + region = aws_region + source: Literal["explicit", "environment", "profile"] = "explicit" + if region is None or not region.strip(): + region = os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") + source = "environment" + if (region is None or not region.strip()) and session is not None: + get_config_variable = getattr(session, "get_config_variable", None) + if callable(get_config_variable): + region = cast("str | None", get_config_variable("region")) + source = "profile" + + if region is None or not region.strip(): + raise OpenAIError( + "Bedrock requires an AWS region. Pass `region` to `bedrock(...)`, or set `AWS_REGION` or " + "`AWS_DEFAULT_REGION`." + ) + + return region.strip(), source diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 4fcae24788..888b480dbb 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -13,6 +13,7 @@ from .._client import OpenAI, AsyncOpenAI from .._compat import model_copy from .._models import SecurityOptions, FinalRequestOptions +from .._provider import _Provider from .._streaming import Stream, AsyncStream from .._exceptions import OpenAIError from .._base_client import DEFAULT_MAX_RETRIES, BaseClient @@ -283,6 +284,7 @@ def copy( api_key: str | Callable[[], str] | None = None, admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -304,6 +306,9 @@ def copy( """ Create a new client instance re-using the same options given to the current client with optional overriding. """ + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `OpenAI`, not on `AzureOpenAI.with_options()`.") + return super().copy( api_key=api_key, admin_api_key=admin_api_key, @@ -603,6 +608,7 @@ def copy( api_key: str | Callable[[], Awaitable[str]] | None = None, admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -624,6 +630,9 @@ def copy( """ Create a new client instance re-using the same options given to the current client with optional overriding. """ + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `AsyncOpenAI`, not on `AsyncAzureOpenAI.with_options()`.") + return super().copy( api_key=api_key, admin_api_key=admin_api_key, diff --git a/src/openai/lib/bedrock.py b/src/openai/lib/bedrock.py index 266a2e9358..466e8ed75e 100644 --- a/src/openai/lib/bedrock.py +++ b/src/openai/lib/bedrock.py @@ -2,8 +2,10 @@ import os import re +import hashlib import inspect -from typing import Any, Mapping, Callable, Awaitable, cast +from typing import Any, Literal, Mapping, Callable, Optional, Awaitable, cast +from dataclasses import field, replace, dataclass from typing_extensions import Self, override import httpx @@ -12,93 +14,382 @@ from .._types import NOT_GIVEN, Timeout, NotGiven from .._utils import is_given from .._client import OpenAI, AsyncOpenAI -from .._models import SecurityOptions, FinalRequestOptions +from .._models import FinalRequestOptions +from .._provider import _Provider, _configure_provider from .._exceptions import OpenAIError from .._base_client import DEFAULT_MAX_RETRIES +from ..providers.bedrock import AwsCredentialsProvider, bedrock, _BedrockProviderRuntime BedrockTokenProvider = Callable[[], str] AsyncBedrockTokenProvider = Callable[[], "str | Awaitable[str]"] +_LegacyAuthMode = Literal["bearer", "token_provider", "aws"] +_LegacyAuthConfiguration = tuple[_LegacyAuthMode, Optional[object]] +_LEGACY_SIGNATURE_KEY = os.urandom(32) -def _normalize_bedrock_base_url(base_url: str | httpx.URL) -> httpx.URL: - """Normalize a Bedrock Responses URL variant back to the provider API root.""" - url = httpx.URL(base_url) - path = url.path.rstrip("/") - responses_match = re.search(r"/responses(?:/.*)?$", path) - if responses_match is not None: - path = path[: responses_match.start()] +@dataclass(frozen=True) +class _LegacyRuntimeSignature: + mode: _LegacyAuthMode + base_url: str + region: str | None + credential_identity: object = field(repr=False) - return url.copy_with(path=path or "/") +@dataclass(frozen=True) +class _LegacyBedrockState: + explicit_api_key: str | None = field(repr=False) + token_provider: BedrockTokenProvider | AsyncBedrockTokenProvider | None = field(repr=False, compare=False) + aws_region: str | None + region_was_explicit: bool + aws_profile: str | None + aws_access_key_id: str | None = field(repr=False) + aws_secret_access_key: str | None = field(repr=False) + aws_session_token: str | None = field(repr=False) + aws_credentials_provider: AwsCredentialsProvider | None = field(repr=False, compare=False) + uses_environment_bearer: bool + environment_bearer_token: str | None = field(repr=False) + uses_region_derived_base_url: bool -def _resolve_bedrock_base_url(base_url: str | httpx.URL | None, aws_region: str | None) -> httpx.URL: - """Resolve Bedrock base URL precedence from explicit, env, then region config.""" - if isinstance(base_url, str) and not base_url.strip(): - base_url = None - if base_url is None: - env_base_url = os.environ.get("AWS_BEDROCK_BASE_URL") - if env_base_url is not None and env_base_url.strip(): - base_url = env_base_url - - if base_url is None: - region = aws_region or os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") - if region is None or not region.strip(): - raise OpenAIError( - "Must provide one of the `base_url` or `aws_region` arguments, or set the " - "`AWS_BEDROCK_BASE_URL`, `AWS_REGION`, or `AWS_DEFAULT_REGION` environment variable." - ) +def _state_api_key(state: _LegacyBedrockState) -> str: + return state.explicit_api_key or (state.environment_bearer_token if state.uses_environment_bearer else "") or "" + + +def _constructor_accepts_keyword(constructor: Callable[..., object], name: str) -> bool: + try: + parameters = inspect.signature(constructor).parameters + except (TypeError, ValueError): + return False + + return name in parameters or any( + parameter.kind is inspect.Parameter.VAR_KEYWORD for parameter in parameters.values() + ) - base_url = f"https://bedrock-mantle.{region}.api.aws/openai/v1" - return _normalize_bedrock_base_url(base_url) +def _configured_region(region: str | None) -> str | None: + configured = region or os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") + return configured.strip() if configured is not None and configured.strip() else None -def _uses_region_derived_bedrock_base_url(base_url: str | httpx.URL | None) -> bool: +def _uses_region_derived_base_url(base_url: str | httpx.URL | None) -> bool: if isinstance(base_url, str) and not base_url.strip(): base_url = None - if base_url is not None: return False - env_base_url = os.environ.get("AWS_BEDROCK_BASE_URL") - return env_base_url is None or not env_base_url.strip() - - -def _bedrock_token_provider(provider: BedrockTokenProvider) -> BedrockTokenProvider: - """Adapt a sync Bedrock token provider to the base client's api_key callback.""" + environment_base_url = os.environ.get("AWS_BEDROCK_BASE_URL") + return environment_base_url is None or not environment_base_url.strip() + + +def _has_explicit_aws_auth( + *, + aws_profile: str | None, + aws_access_key_id: str | None, + aws_secret_access_key: str | None, + aws_session_token: str | None, + aws_credentials_provider: AwsCredentialsProvider | None, +) -> bool: + return any( + value is not None + for value in ( + aws_profile, + aws_access_key_id, + aws_secret_access_key, + aws_session_token, + aws_credentials_provider, + ) + ) - def get_token() -> str: - token = cast(object, provider()) - if not isinstance(token, str) or not token: - raise ValueError(f"Expected `bedrock_token_provider` argument to return a string but it returned {token}") - return token +def _environment_bearer_token() -> str: + token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + if not token: + raise OpenAIError( + "Could not find credentials for Bedrock. Set `AWS_BEARER_TOKEN_BEDROCK` or configure the default " + "AWS credential chain." + ) + return token + + +def _legacy_provider( + *, + api_key: str | None, + token_provider: BedrockTokenProvider | AsyncBedrockTokenProvider | None, + aws_region: str | None, + aws_profile: str | None, + aws_access_key_id: str | None, + aws_secret_access_key: str | None, + aws_session_token: str | None, + aws_credentials_provider: AwsCredentialsProvider | None, + base_url: str | httpx.URL | None, + region_was_explicit: bool | None = None, +) -> tuple[_Provider, _LegacyBedrockState, str]: + if callable(cast(object, api_key)): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + if api_key == "": + raise OpenAIError("The `api_key` argument must not be empty.") + if api_key is not None and token_provider is not None: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) - return get_token + explicit_aws_auth = _has_explicit_aws_auth( + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + ) + if (api_key is not None or token_provider is not None) and explicit_aws_auth: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) + environment_token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + uses_environment_bearer = ( + api_key is None and token_provider is None and not explicit_aws_auth and bool(environment_token) + ) + resolved_region = _configured_region(aws_region) + uses_region_derived_base_url = _uses_region_derived_base_url(base_url) -def _async_bedrock_token_provider(provider: AsyncBedrockTokenProvider) -> Callable[[], Awaitable[str]]: - """Adapt a sync or async Bedrock token provider to the async api_key callback.""" + provider_base_url: str | httpx.URL | None | NotGiven + if isinstance(base_url, str) and not base_url.strip(): + provider_base_url = None + elif base_url is None: + provider_base_url = NOT_GIVEN + else: + provider_base_url = base_url + + provider = bedrock( + region=aws_region, + base_url=provider_base_url, + api_key=api_key if api_key is not None else environment_token if uses_environment_bearer else NOT_GIVEN, + token_provider=token_provider, + access_key_id=aws_access_key_id, + secret_access_key=aws_secret_access_key, + session_token=aws_session_token, + profile=aws_profile, + credential_provider=aws_credentials_provider, + ) + state = _LegacyBedrockState( + explicit_api_key=api_key, + token_provider=token_provider, + aws_region=resolved_region, + region_was_explicit=( + bool(aws_region and aws_region.strip()) if region_was_explicit is None else region_was_explicit + ), + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + uses_environment_bearer=uses_environment_bearer, + environment_bearer_token=environment_token if uses_environment_bearer else None, + uses_region_derived_base_url=uses_region_derived_base_url, + ) + return provider, state, api_key or (environment_token if uses_environment_bearer else "") or "" + + +def _copy_configuration( + client: BedrockOpenAI | AsyncBedrockOpenAI, + *, + api_key: str | None, + token_provider: BedrockTokenProvider | AsyncBedrockTokenProvider | None, + aws_region: str | None, + aws_profile: str | None, + aws_access_key_id: str | None, + aws_secret_access_key: str | None, + aws_session_token: str | None, + aws_credentials_provider: AwsCredentialsProvider | None, + base_url: str | httpx.URL | None, +) -> tuple[dict[str, object], _Provider | None, _LegacyBedrockState | None]: + _synchronize_legacy_routing_state(client) + state = client._bedrock_state + current_api_key = client.api_key or "" + api_key_was_mutated = state.token_provider is None and current_api_key != _state_api_key(state) + aws_override = _has_explicit_aws_auth( + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + ) + explicit_bearer_override = api_key is not None or token_provider is not None + if explicit_bearer_override and aws_override: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) - async def get_token() -> str: - token = cast(object, provider()) - if inspect.isawaitable(token): - token = await token + effective_api_key = ( + api_key + if api_key is not None + else current_api_key + if api_key_was_mutated and token_provider is None and not aws_override + else None + ) + bearer_override = effective_api_key is not None or token_provider is not None + + routing_override = aws_region is not None or base_url is not None + if not bearer_override and not aws_override and not routing_override: + _refresh_legacy_provider_runtime(client) + return {}, client._bedrock_provider, client._bedrock_state + + if bearer_override: + next_api_key = effective_api_key + next_token_provider = token_provider + next_profile = None + next_access_key_id = None + next_secret_access_key = None + next_session_token = None + next_credentials_provider = None + elif aws_override: + next_api_key = None + next_token_provider = None + next_profile = aws_profile + next_access_key_id = aws_access_key_id + next_secret_access_key = aws_secret_access_key + next_session_token = aws_session_token + next_credentials_provider = aws_credentials_provider + else: + next_api_key = state.explicit_api_key + next_token_provider = state.token_provider + if state.uses_environment_bearer: + next_api_key = state.environment_bearer_token or _environment_bearer_token() + next_token_provider = None + next_profile = state.aws_profile + next_access_key_id = state.aws_access_key_id + next_secret_access_key = state.aws_secret_access_key + next_session_token = state.aws_session_token + next_credentials_provider = state.aws_credentials_provider - if not isinstance(token, str) or not token: - raise ValueError(f"Expected `bedrock_token_provider` argument to return a string but it returned {token}") + next_region = aws_region if aws_region is not None else client.aws_region + next_region_was_explicit = aws_region is not None or state.region_was_explicit + if aws_profile is not None and aws_region is None and not state.region_was_explicit: + next_region = None - return token + if base_url is not None: + next_base_url: str | httpx.URL | None = base_url + elif state.uses_region_derived_base_url: + next_base_url = "" + else: + next_base_url = client.base_url + + provider_kwargs: dict[str, object] = { + "api_key": next_api_key, + "bedrock_token_provider": next_token_provider, + "aws_region": next_region, + "aws_profile": next_profile, + "aws_access_key_id": next_access_key_id, + "aws_secret_access_key": next_secret_access_key, + "aws_session_token": next_session_token, + "aws_credentials_provider": next_credentials_provider, + "base_url": next_base_url, + } + if _constructor_accepts_keyword(client.__class__.__init__, "_region_was_explicit"): + provider_kwargs["_region_was_explicit"] = next_region_was_explicit + + return provider_kwargs, None, None + + +def _legacy_runtime_signature( + client: BedrockOpenAI | AsyncBedrockOpenAI, + configuration: _LegacyAuthConfiguration, +) -> _LegacyRuntimeSignature: + mode, credential = configuration + credential_identity: object = ( + hashlib.blake2s(credential.encode(), key=_LEGACY_SIGNATURE_KEY).digest() + if isinstance(credential, str) + else id(credential) + ) + return _LegacyRuntimeSignature( + mode=mode, + base_url=str(client.base_url), + region=client.aws_region, + credential_identity=credential_identity, + ) + + +def _provider_for_legacy_client( + client: BedrockOpenAI | AsyncBedrockOpenAI, + configuration: _LegacyAuthConfiguration, +) -> _Provider: + mode, credential = configuration + if mode == "bearer": + if not isinstance(credential, str) or not credential: + raise OpenAIError("The Bedrock bearer credential must not be empty.") + return bedrock( + region=client.aws_region, + base_url=client.base_url, + api_key=credential, + ) + if mode == "token_provider": + return bedrock( + region=client.aws_region, + base_url=client.base_url, + token_provider=cast("AsyncBedrockTokenProvider", credential), + ) - return get_token + state = client._bedrock_state + return bedrock( + region=client.aws_region, + base_url=client.base_url, + profile=state.aws_profile, + access_key_id=state.aws_access_key_id, + secret_access_key=state.aws_secret_access_key, + session_token=state.aws_session_token, + credential_provider=state.aws_credentials_provider, + ) + + +def _synchronize_legacy_routing_state(client: BedrockOpenAI | AsyncBedrockOpenAI) -> None: + previous_signature = client._bedrock_runtime_signature + base_url_changed = str(client.base_url) != previous_signature.base_url + region_changed = client.aws_region != previous_signature.region + if base_url_changed: + client._bedrock_state = replace(client._bedrock_state, uses_region_derived_base_url=False) + client._uses_region_derived_base_url = False + if region_changed: + client._bedrock_state = replace( + client._bedrock_state, + aws_region=client.aws_region, + region_was_explicit=client.aws_region is not None, + ) + if client._bedrock_state.uses_region_derived_base_url and client.aws_region is not None: + client.base_url = f"https://bedrock-mantle.{client.aws_region}.api.aws/openai/v1" + + +def _refresh_legacy_provider_runtime(client: BedrockOpenAI | AsyncBedrockOpenAI) -> None: + _synchronize_legacy_routing_state(client) + configuration = client._legacy_auth_configuration() + signature = _legacy_runtime_signature(client, configuration) + if signature == client._bedrock_runtime_signature: + return + + provider = _provider_for_legacy_client(client, configuration) + client._bedrock_provider = provider + client._provider = provider + client._provider_runtime = _configure_provider(provider) + if ( + isinstance(client._provider_runtime, _BedrockProviderRuntime) + and client.aws_region is None + and client._provider_runtime.region is not None + ): + client.aws_region = client._provider_runtime.region + client._bedrock_state = replace(client._bedrock_state, aws_region=client.aws_region) + client._bedrock_runtime_signature = _legacy_runtime_signature(client, configuration) class BedrockOpenAI(OpenAI): - """API client for Amazon Bedrock's OpenAI-compatible endpoint.""" + """Compatibility client for Amazon Bedrock's OpenAI-compatible endpoint.""" + _bedrock_provider: _Provider + _bedrock_state: _LegacyBedrockState _bedrock_token_provider: BedrockTokenProvider | None _uses_region_derived_base_url: bool + _bedrock_runtime_signature: _LegacyRuntimeSignature aws_region: str | None def __init__( @@ -107,6 +398,11 @@ def __init__( api_key: str | None = None, bedrock_token_provider: BedrockTokenProvider | None = None, aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -119,44 +415,35 @@ def __init__( http_client: httpx.Client | None = None, _strict_response_validation: bool = False, _enforce_credentials: bool = True, + _provider: _Provider | None = None, + _state: _LegacyBedrockState | None = None, + _region_was_explicit: bool | None = None, ) -> None: - """Construct a new synchronous Amazon Bedrock client instance. - - This automatically infers the following arguments from their corresponding environment variables if they are not provided: - - `api_key` from `AWS_BEARER_TOKEN_BEDROCK` - - `aws_region` from `AWS_REGION` or `AWS_DEFAULT_REGION` when `base_url` and `AWS_BEDROCK_BASE_URL` are not set - - `base_url` from `AWS_BEDROCK_BASE_URL` - - `bedrock_token_provider` is invoked before each request when provided. - """ - if api_key is None and bedrock_token_provider is None: - api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") - - if callable(cast(object, api_key)): - raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") - - if api_key is not None and bedrock_token_provider is not None: - raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") - - if _enforce_credentials and not api_key and bedrock_token_provider is None: - raise OpenAIError( - "Missing credentials. Please pass an `api_key` or `bedrock_token_provider`, or set the " - "`AWS_BEARER_TOKEN_BEDROCK` environment variable." + if _provider is None or _state is None: + _provider, _state, public_api_key = _legacy_provider( + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, + region_was_explicit=_region_was_explicit, + ) + else: + public_api_key = ( + _state.explicit_api_key + or (_state.environment_bearer_token if _state.uses_environment_bearer else "") + or "" ) - - self._bedrock_token_provider = bedrock_token_provider - self._uses_region_derived_base_url = _uses_region_derived_bedrock_base_url(base_url) - self.aws_region = aws_region super().__init__( - api_key=_bedrock_token_provider(bedrock_token_provider) - if bedrock_token_provider is not None - else api_key or "", - admin_api_key="", + provider=_provider, organization=organization, project=project, webhook_secret=webhook_secret, - base_url=_resolve_bedrock_base_url(base_url, aws_region), websocket_base_url=websocket_base_url, timeout=timeout, max_retries=max_retries, @@ -167,22 +454,57 @@ def __init__( _enforce_credentials=False, ) - @override - def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: - if security.get("bearer_auth", False) or security.get("admin_api_key_auth", False): - return self._bearer_auth - - return {} + self._bedrock_provider = _provider + self._bedrock_state = _state + self._bedrock_token_provider = cast("BedrockTokenProvider | None", _state.token_provider) + self._uses_region_derived_base_url = _state.uses_region_derived_base_url + canonical_region = re.fullmatch(r"bedrock-mantle\.([a-z0-9-]+)\.api\.aws", self.base_url.host) + provider_region = ( + self._provider_runtime.region if isinstance(self._provider_runtime, _BedrockProviderRuntime) else None + ) + self.aws_region = ( + _state.aws_region + or provider_region + or (canonical_region.group(1) if canonical_region is not None else None) + ) + self._bedrock_state = replace(_state, aws_region=self.aws_region) + self.api_key = public_api_key or "" + self._bedrock_runtime_signature = _legacy_runtime_signature(self, self._legacy_auth_configuration()) - @override - def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + def _legacy_auth_configuration(self) -> _LegacyAuthConfiguration: + if self._bedrock_token_provider is not None: + return ("token_provider", self._bedrock_token_provider) if ( - self._api_key_provider is not None - and options.security.get("admin_api_key_auth", False) - and not options.security.get("bearer_auth", False) + self._bedrock_state.explicit_api_key is not None + or self._bedrock_state.uses_environment_bearer + or self.api_key ): - self._refresh_api_key() + return ("bearer", self.api_key) + return ("aws", None) + + def _uses_aws_auth(self) -> bool: + return ( + self._bedrock_state.explicit_api_key is None + and not self.api_key + and self._bedrock_token_provider is None + and not self._bedrock_state.uses_environment_bearer + ) + + @override + def _refresh_api_key(self) -> str: + if self._bedrock_state.uses_environment_bearer: + captured = self._bedrock_state.environment_bearer_token or "" + return self.api_key if self.api_key and self.api_key != captured else captured + if self._bedrock_token_provider is not None: + token = cast(object, self._bedrock_token_provider()) + if not isinstance(token, str) or not token: + raise ValueError("Expected `bedrock_token_provider` argument to return a non-empty string.") + return token + return self.api_key + @override + def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + _refresh_legacy_provider_runtime(self) return super()._prepare_options(options) @override @@ -192,8 +514,14 @@ def copy( api_key: str | BedrockTokenProvider | None = None, admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, bedrock_token_provider: BedrockTokenProvider | None = None, aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -209,71 +537,93 @@ def copy( _enforce_credentials: bool | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: + if callable(api_key): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `OpenAI`, not on `BedrockOpenAI.with_options()`.") + if admin_api_key is not None or workload_identity is not None: + raise OpenAIError("BedrockOpenAI only supports Bedrock bearer token or AWS credential authentication.") if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - if callable(api_key): - raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") - - if admin_api_key is not None or workload_identity is not None: - raise OpenAIError("BedrockOpenAI only supports Bedrock bearer token authentication.") - - if api_key is not None and bedrock_token_provider is not None: - raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") - headers = self._custom_headers if default_headers is not None: headers = {**headers, **default_headers} elif set_default_headers is not None: headers = set_default_headers - params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query - if api_key is not None: - next_token_provider = None - elif bedrock_token_provider is not None: - next_token_provider = bedrock_token_provider - else: - next_token_provider = self._bedrock_token_provider - - next_api_key = api_key if api_key is not None else (None if next_token_provider is not None else self.api_key) - next_base_url = base_url - if next_base_url is None and not (aws_region is not None and self._uses_region_derived_base_url): - next_base_url = self.base_url - - return self.__class__( - api_key=next_api_key, - bedrock_token_provider=next_token_provider, - aws_region=aws_region if aws_region is not None else self.aws_region, - organization=organization if organization is not None else self.organization, - project=project if project is not None else self.project, - webhook_secret=webhook_secret if webhook_secret is not None else self.webhook_secret, - websocket_base_url=websocket_base_url if websocket_base_url is not None else self.websocket_base_url, - base_url=next_base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client or self._client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, - **_extra_kwargs, + provider_kwargs, inherited_provider, inherited_state = _copy_configuration( + self, + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, ) + constructor_kwargs: dict[str, Any] = { + **provider_kwargs, + "organization": organization if organization is not None else self.organization, + "project": project if project is not None else self.project, + "webhook_secret": webhook_secret if webhook_secret is not None else self.webhook_secret, + "websocket_base_url": websocket_base_url if websocket_base_url is not None else self.websocket_base_url, + "timeout": self.timeout if isinstance(timeout, NotGiven) else timeout, + "http_client": http_client or self._client, + "max_retries": max_retries if is_given(max_retries) else self.max_retries, + "default_headers": headers, + "default_query": params, + "_enforce_credentials": True if _enforce_credentials is None else _enforce_credentials, + **_extra_kwargs, + } + if inherited_provider is not None and _constructor_accepts_keyword(self.__class__.__init__, "_provider"): + constructor_kwargs["_provider"] = inherited_provider + constructor_kwargs["_state"] = inherited_state + elif inherited_provider is not None: + constructor_kwargs.update( + api_key=self._bedrock_state.explicit_api_key or self._bedrock_state.environment_bearer_token, + bedrock_token_provider=self._bedrock_state.token_provider, + aws_region=self._bedrock_state.aws_region, + aws_profile=self._bedrock_state.aws_profile, + aws_access_key_id=self._bedrock_state.aws_access_key_id, + aws_secret_access_key=self._bedrock_state.aws_secret_access_key, + aws_session_token=self._bedrock_state.aws_session_token, + aws_credentials_provider=self._bedrock_state.aws_credentials_provider, + base_url="" if self._bedrock_state.uses_region_derived_base_url else self.base_url, + ) + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if _constructor_accepts_keyword(self.__class__.__init__, name) + } + elif self.__class__ is not BedrockOpenAI: + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if value is not None or _constructor_accepts_keyword(self.__class__.__init__, name) + } + return self.__class__(**constructor_kwargs) with_options = copy class AsyncBedrockOpenAI(AsyncOpenAI): - """Async API client for Amazon Bedrock's OpenAI-compatible endpoint.""" + """Async compatibility client for Amazon Bedrock's OpenAI-compatible endpoint.""" + _bedrock_provider: _Provider + _bedrock_state: _LegacyBedrockState _bedrock_token_provider: AsyncBedrockTokenProvider | None _uses_region_derived_base_url: bool + _bedrock_runtime_signature: _LegacyRuntimeSignature aws_region: str | None def __init__( @@ -282,6 +632,11 @@ def __init__( api_key: str | None = None, bedrock_token_provider: AsyncBedrockTokenProvider | None = None, aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -294,46 +649,35 @@ def __init__( http_client: httpx.AsyncClient | None = None, _strict_response_validation: bool = False, _enforce_credentials: bool = True, + _provider: _Provider | None = None, + _state: _LegacyBedrockState | None = None, + _region_was_explicit: bool | None = None, ) -> None: - """Construct a new asynchronous Amazon Bedrock client instance. - - This automatically infers the following arguments from their corresponding environment variables if they are not provided: - - `api_key` from `AWS_BEARER_TOKEN_BEDROCK` - - `aws_region` from `AWS_REGION` or `AWS_DEFAULT_REGION` when `base_url` and `AWS_BEDROCK_BASE_URL` are not set - - `base_url` from `AWS_BEDROCK_BASE_URL` - - `bedrock_token_provider` is invoked before each request when provided. - """ - if api_key is None and bedrock_token_provider is None: - api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") - - if callable(cast(object, api_key)): - raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") - - if api_key is not None and bedrock_token_provider is not None: - raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") - - if _enforce_credentials and not api_key and bedrock_token_provider is None: - raise OpenAIError( - "Missing credentials. Please pass an `api_key` or `bedrock_token_provider`, or set the " - "`AWS_BEARER_TOKEN_BEDROCK` environment variable." + if _provider is None or _state is None: + _provider, _state, public_api_key = _legacy_provider( + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, + region_was_explicit=_region_was_explicit, + ) + else: + public_api_key = ( + _state.explicit_api_key + or (_state.environment_bearer_token if _state.uses_environment_bearer else "") + or "" ) - - self._bedrock_token_provider = bedrock_token_provider - self._uses_region_derived_base_url = _uses_region_derived_bedrock_base_url(base_url) - self.aws_region = aws_region super().__init__( - api_key=( - _async_bedrock_token_provider(bedrock_token_provider) - if bedrock_token_provider is not None - else api_key or "" - ), - admin_api_key="", + provider=_provider, organization=organization, project=project, webhook_secret=webhook_secret, - base_url=_resolve_bedrock_base_url(base_url, aws_region), websocket_base_url=websocket_base_url, timeout=timeout, max_retries=max_retries, @@ -344,22 +688,59 @@ def __init__( _enforce_credentials=False, ) - @override - def _auth_headers(self, security: SecurityOptions) -> dict[str, str]: - if security.get("bearer_auth", False) or security.get("admin_api_key_auth", False): - return self._bearer_auth - - return {} + self._bedrock_provider = _provider + self._bedrock_state = _state + self._bedrock_token_provider = cast("AsyncBedrockTokenProvider | None", _state.token_provider) + self._uses_region_derived_base_url = _state.uses_region_derived_base_url + canonical_region = re.fullmatch(r"bedrock-mantle\.([a-z0-9-]+)\.api\.aws", self.base_url.host) + provider_region = ( + self._provider_runtime.region if isinstance(self._provider_runtime, _BedrockProviderRuntime) else None + ) + self.aws_region = ( + _state.aws_region + or provider_region + or (canonical_region.group(1) if canonical_region is not None else None) + ) + self._bedrock_state = replace(_state, aws_region=self.aws_region) + self.api_key = public_api_key or "" + self._bedrock_runtime_signature = _legacy_runtime_signature(self, self._legacy_auth_configuration()) - @override - async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + def _legacy_auth_configuration(self) -> _LegacyAuthConfiguration: + if self._bedrock_token_provider is not None: + return ("token_provider", self._bedrock_token_provider) if ( - self._api_key_provider is not None - and options.security.get("admin_api_key_auth", False) - and not options.security.get("bearer_auth", False) + self._bedrock_state.explicit_api_key is not None + or self._bedrock_state.uses_environment_bearer + or self.api_key ): - await self._refresh_api_key() + return ("bearer", self.api_key) + return ("aws", None) + + def _uses_aws_auth(self) -> bool: + return ( + self._bedrock_state.explicit_api_key is None + and not self.api_key + and self._bedrock_token_provider is None + and not self._bedrock_state.uses_environment_bearer + ) + + @override + async def _refresh_api_key(self) -> str: + if self._bedrock_state.uses_environment_bearer: + captured = self._bedrock_state.environment_bearer_token or "" + return self.api_key if self.api_key and self.api_key != captured else captured + if self._bedrock_token_provider is not None: + token = cast(object, self._bedrock_token_provider()) + if inspect.isawaitable(token): + token = await token + if not isinstance(token, str) or not token: + raise ValueError("Expected `bedrock_token_provider` argument to return a non-empty string.") + return token + return self.api_key + @override + async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: + _refresh_legacy_provider_runtime(self) return await super()._prepare_options(options) @override @@ -369,8 +750,14 @@ def copy( api_key: str | AsyncBedrockTokenProvider | None = None, admin_api_key: str | None = None, workload_identity: WorkloadIdentity | None = None, + provider: _Provider | None | NotGiven = NOT_GIVEN, bedrock_token_provider: AsyncBedrockTokenProvider | None = None, aws_region: str | None = None, + aws_profile: str | None = None, + aws_access_key_id: str | None = None, + aws_secret_access_key: str | None = None, + aws_session_token: str | None = None, + aws_credentials_provider: AwsCredentialsProvider | None = None, organization: str | None = None, project: str | None = None, webhook_secret: str | None = None, @@ -386,61 +773,89 @@ def copy( _enforce_credentials: bool | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: + if callable(api_key): + raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") + if not isinstance(provider, NotGiven): + raise OpenAIError("Configure `provider` on `AsyncOpenAI`, not on `AsyncBedrockOpenAI.with_options()`.") + if admin_api_key is not None or workload_identity is not None: + raise OpenAIError("AsyncBedrockOpenAI only supports Bedrock bearer token or AWS credential authentication.") if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - if callable(api_key): - raise OpenAIError("Pass refreshable Bedrock credentials via `bedrock_token_provider`, not `api_key`.") - - if admin_api_key is not None or workload_identity is not None: - raise OpenAIError("AsyncBedrockOpenAI only supports Bedrock bearer token authentication.") - - if api_key is not None and bedrock_token_provider is not None: - raise OpenAIError("The `api_key` and `bedrock_token_provider` arguments are mutually exclusive.") - headers = self._custom_headers if default_headers is not None: headers = {**headers, **default_headers} elif set_default_headers is not None: headers = set_default_headers - params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query - if api_key is not None: - next_token_provider = None - elif bedrock_token_provider is not None: - next_token_provider = bedrock_token_provider - else: - next_token_provider = self._bedrock_token_provider - - next_api_key = api_key if api_key is not None else (None if next_token_provider is not None else self.api_key) - next_base_url = base_url - if next_base_url is None and not (aws_region is not None and self._uses_region_derived_base_url): - next_base_url = self.base_url - - return self.__class__( - api_key=next_api_key, - bedrock_token_provider=next_token_provider, - aws_region=aws_region if aws_region is not None else self.aws_region, - organization=organization if organization is not None else self.organization, - project=project if project is not None else self.project, - webhook_secret=webhook_secret if webhook_secret is not None else self.webhook_secret, - websocket_base_url=websocket_base_url if websocket_base_url is not None else self.websocket_base_url, - base_url=next_base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client or self._client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - _enforce_credentials=True if _enforce_credentials is None else _enforce_credentials, - **_extra_kwargs, + provider_kwargs, inherited_provider, inherited_state = _copy_configuration( + self, + api_key=api_key, + token_provider=bedrock_token_provider, + aws_region=aws_region, + aws_profile=aws_profile, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key, + aws_session_token=aws_session_token, + aws_credentials_provider=aws_credentials_provider, + base_url=base_url, ) + constructor_kwargs: dict[str, Any] = { + **provider_kwargs, + "organization": organization if organization is not None else self.organization, + "project": project if project is not None else self.project, + "webhook_secret": webhook_secret if webhook_secret is not None else self.webhook_secret, + "websocket_base_url": websocket_base_url if websocket_base_url is not None else self.websocket_base_url, + "timeout": self.timeout if isinstance(timeout, NotGiven) else timeout, + "http_client": http_client or self._client, + "max_retries": max_retries if is_given(max_retries) else self.max_retries, + "default_headers": headers, + "default_query": params, + "_enforce_credentials": True if _enforce_credentials is None else _enforce_credentials, + **_extra_kwargs, + } + if inherited_provider is not None and _constructor_accepts_keyword(self.__class__.__init__, "_provider"): + constructor_kwargs["_provider"] = inherited_provider + constructor_kwargs["_state"] = inherited_state + elif inherited_provider is not None: + constructor_kwargs.update( + api_key=self._bedrock_state.explicit_api_key or self._bedrock_state.environment_bearer_token, + bedrock_token_provider=self._bedrock_state.token_provider, + aws_region=self._bedrock_state.aws_region, + aws_profile=self._bedrock_state.aws_profile, + aws_access_key_id=self._bedrock_state.aws_access_key_id, + aws_secret_access_key=self._bedrock_state.aws_secret_access_key, + aws_session_token=self._bedrock_state.aws_session_token, + aws_credentials_provider=self._bedrock_state.aws_credentials_provider, + base_url="" if self._bedrock_state.uses_region_derived_base_url else self.base_url, + ) + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if _constructor_accepts_keyword(self.__class__.__init__, name) + } + elif self.__class__ is not AsyncBedrockOpenAI: + constructor_kwargs = { + name: value + for name, value in constructor_kwargs.items() + if value is not None or _constructor_accepts_keyword(self.__class__.__init__, name) + } + return self.__class__(**constructor_kwargs) with_options = copy + + +__all__ = [ + "BedrockOpenAI", + "AsyncBedrockOpenAI", + "BedrockTokenProvider", + "AsyncBedrockTokenProvider", + "AwsCredentialsProvider", +] diff --git a/src/openai/providers/__init__.py b/src/openai/providers/__init__.py new file mode 100644 index 0000000000..bb5bcdbd9e --- /dev/null +++ b/src/openai/providers/__init__.py @@ -0,0 +1,3 @@ +from .bedrock import bedrock as bedrock + +__all__ = ["bedrock"] diff --git a/src/openai/providers/bedrock.py b/src/openai/providers/bedrock.py new file mode 100644 index 0000000000..5cee093bfd --- /dev/null +++ b/src/openai/providers/bedrock.py @@ -0,0 +1,411 @@ +from __future__ import annotations + +import os +import re +import inspect +from typing import Literal, Callable, Awaitable, cast +from dataclasses import field, dataclass + +import httpx + +from .._types import NOT_GIVEN, NotGiven +from .._utils import asyncify +from .._models import FinalRequestOptions +from .._provider import _Provider, _create_provider, _ProviderRuntime +from .._exceptions import OpenAIError +from ..lib._bedrock_auth import ( + BedrockAwsAuth, + BedrockAwsAuthConfig, + AwsCredentialsProvider, +) + +BedrockTokenProvider = Callable[[], "str | Awaitable[str]"] + +_AWS_SIGNING_HEADERS = ("authorization", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token") +_CANONICAL_BEDROCK_HOST = re.compile(r"^bedrock-mantle\.([a-z0-9-]+)\.api\.aws$", re.IGNORECASE) + + +def _normalize_optional_string(value: str | None) -> str | None: + if value is None: + return None + + normalized = value.strip() + return normalized or None + + +def _normalize_base_url(base_url: str | httpx.URL) -> httpx.URL: + url = httpx.URL(base_url) + path = url.path.rstrip("/") + responses_match = re.search(r"/responses(?:/.*)?$", path) + if responses_match is not None: + path = path[: responses_match.start()] + + return url.copy_with(path=path or "/") + + +def _same_origin(left: httpx.URL, right: httpx.URL) -> bool: + return (left.scheme, left.host, left.port) == (right.scheme, right.host, right.port) + + +def _body_for_signing(request: httpx.Request) -> bytes: + try: + return request.content + except httpx.RequestNotRead as exc: + raise OpenAIError( + "Bedrock SigV4 authentication requires a replayable request body. " + "Buffer the body before sending or use bearer authentication." + ) from exc + + +def _assert_provider_owns_authorization(request: httpx.Request) -> None: + if "Authorization" in request.headers: + raise OpenAIError("Bedrock provider authentication cannot be combined with a custom `Authorization` header.") + + +def _without_redirects(options: FinalRequestOptions) -> FinalRequestOptions: + if options.follow_redirects: + raise OpenAIError( + "Bedrock SigV4 authentication does not support automatic redirects. " + "Send a new request to the redirect target so it can be signed again." + ) + options.follow_redirects = False + return options + + +class _BedrockBearerAuth: + def __init__(self, token_provider: BedrockTokenProvider, *, base_url: httpx.URL) -> None: + self._token_provider = token_provider + self._base_url = base_url + + def _validate_request(self, request: httpx.Request) -> None: + _assert_provider_owns_authorization(request) + if not _same_origin(request.url, self._base_url): + raise OpenAIError( + "Refusing to authenticate a Bedrock request for an origin other than the configured provider URL." + ) + + def _resolve_token(self) -> str: + try: + token = cast(object, self._token_provider()) + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError("Failed to resolve a bearer credential for Bedrock.") from exc + + if inspect.isawaitable(token): + close = getattr(token, "close", None) + if callable(close): + close() + raise OpenAIError("An async Bedrock token provider requires `AsyncOpenAI`.") + if not isinstance(token, str) or not token.strip(): + raise OpenAIError("The Bedrock bearer credential provider must return a non-empty string.") + return token + + async def _resolve_token_async(self) -> str: + try: + token = cast(object, self._token_provider()) + if inspect.isawaitable(token): + token = await token + except OpenAIError: + raise + except Exception as exc: + raise OpenAIError("Failed to resolve a bearer credential for Bedrock.") from exc + + if not isinstance(token, str) or not token.strip(): + raise OpenAIError("The Bedrock bearer credential provider must return a non-empty string.") + return token + + def prepare_request(self, request: httpx.Request) -> None: + self._validate_request(request) + request.headers["Authorization"] = f"Bearer {self._resolve_token()}" + + async def prepare_async_request(self, request: httpx.Request) -> None: + self._validate_request(request) + request.headers["Authorization"] = f"Bearer {await self._resolve_token_async()}" + + +class _BedrockSigV4Auth: + def __init__( + self, + *, + config: BedrockAwsAuthConfig, + base_url: httpx.URL, + auth: BedrockAwsAuth | None = None, + ) -> None: + self._config = config + self._base_url = base_url + self._auth = auth + + def _validate_request(self, request: httpx.Request) -> bytes: + _assert_provider_owns_authorization(request) + if not _same_origin(request.url, self._base_url): + raise OpenAIError( + "Refusing to sign a Bedrock request for an origin other than the configured provider URL." + ) + + endpoint_region_match = _CANONICAL_BEDROCK_HOST.fullmatch(request.url.host) + if endpoint_region_match is not None and endpoint_region_match.group(1) != self._config.region: + raise OpenAIError( + f"The Bedrock endpoint region `{endpoint_region_match.group(1)}` does not match the " + f"SigV4 region `{self._config.region}`." + ) + + return _body_for_signing(request) + + def _sign(self, request: httpx.Request, *, auth: BedrockAwsAuth, body: bytes) -> None: + for header in _AWS_SIGNING_HEADERS: + request.headers.pop(header, None) + + signed_headers = auth.sign( + method=request.method, + url=str(request.url), + headers=dict(request.headers), + body=body, + ) + request.headers.clear() + request.headers.update(signed_headers) + + def prepare_request(self, request: httpx.Request) -> None: + body = self._validate_request(request) + if self._auth is None: + self._auth = BedrockAwsAuth(self._config) + self._sign(request, auth=self._auth, body=body) + + async def prepare_async_request(self, request: httpx.Request) -> None: + body = self._validate_request(request) + if self._auth is None: + self._auth = await asyncify(BedrockAwsAuth)(self._config) + + signed_headers = await asyncify(self._auth.sign)( + method=request.method, + url=str(request.url), + headers={ + name: value for name, value in request.headers.items() if name.lower() not in _AWS_SIGNING_HEADERS + }, + body=body, + ) + request.headers.clear() + request.headers.update(signed_headers) + + +@dataclass +class _BedrockProviderRuntime(_ProviderRuntime): + region: str | None = None + + +@dataclass(frozen=True) +class _BedrockProviderDefinition: + configured_region: str | None + region_source: Literal["explicit", "environment"] | None + configured_base_url: httpx.URL | None + api_key: str | None = field(default=None, repr=False) + token_provider: BedrockTokenProvider | None = field(default=None, repr=False, compare=False) + use_environment_bearer: bool = False + profile: str | None = None + access_key_id: str | None = field(default=None, repr=False) + secret_access_key: str | None = field(default=None, repr=False) + session_token: str | None = field(default=None, repr=False) + credential_provider: AwsCredentialsProvider | None = field(default=None, repr=False, compare=False) + name: str = field(default="bedrock", init=False) + + def _aws_source(self) -> Literal["static", "profile", "provider", "default"]: + if self.access_key_id is not None: + return "static" + if self.profile is not None: + return "profile" + if self.credential_provider is not None: + return "provider" + return "default" + + def _resolve_aws_auth(self) -> tuple[BedrockAwsAuthConfig, BedrockAwsAuth | None]: + if self.configured_region is not None: + return ( + BedrockAwsAuthConfig( + region=self.configured_region, + source=self._aws_source(), + region_source=self.region_source or "explicit", + profile=self.profile, + access_key_id=self.access_key_id, + secret_access_key=self.secret_access_key, + session_token=self.session_token, + credentials_provider=self.credential_provider, + ), + None, + ) + + auth = BedrockAwsAuth.resolve( + region=None, + profile=self.profile, + access_key_id=self.access_key_id, + secret_access_key=self.secret_access_key, + session_token=self.session_token, + credentials_provider=self.credential_provider, + ) + return auth.config, auth + + def configure(self) -> _ProviderRuntime: + def environment_token() -> str: + token = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") + if not token: + raise OpenAIError( + "Could not find credentials for Bedrock. Pass a bearer credential or AWS credentials to " + "`bedrock(...)`, set `AWS_BEARER_TOKEN_BEDROCK`, or configure the default AWS credential chain." + ) + return token + + auth: _BedrockBearerAuth | _BedrockSigV4Auth | None = None + bearer_provider: BedrockTokenProvider | None = None + if self.api_key is not None: + bearer_provider = lambda: self.api_key or "" + region = self.configured_region + elif self.token_provider is not None: + bearer_provider = self.token_provider + region = self.configured_region + elif self.use_environment_bearer: + bearer_provider = environment_token + region = self.configured_region + else: + aws_config, aws_auth = self._resolve_aws_auth() + region = aws_config.region + base_url = self.configured_base_url or _normalize_base_url( + f"https://bedrock-mantle.{region}.api.aws/openai/v1" + ) + auth = _BedrockSigV4Auth(config=aws_config, base_url=base_url, auth=aws_auth) + + if self.configured_base_url is not None: + base_url = self.configured_base_url + elif region is not None: + base_url = _normalize_base_url(f"https://bedrock-mantle.{region}.api.aws/openai/v1") + else: + raise OpenAIError( + "Bedrock requires an AWS region. Pass `region` to `bedrock(...)`, or set `AWS_REGION` or " + "`AWS_DEFAULT_REGION`." + ) + + if bearer_provider is not None: + auth = _BedrockBearerAuth(bearer_provider, base_url=base_url) + + assert auth is not None + if isinstance(auth, _BedrockSigV4Auth): + return _BedrockProviderRuntime( + name=self.name, + base_url=base_url, + region=region, + transform_request=_without_redirects, + prepare_request=auth.prepare_request, + prepare_async_request=auth.prepare_async_request, + ) + + return _BedrockProviderRuntime( + name=self.name, + base_url=base_url, + region=region, + prepare_request=auth.prepare_request, + prepare_async_request=auth.prepare_async_request, + ) + + +def bedrock( + *, + region: str | None = None, + base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN, + api_key: str | None | NotGiven = NOT_GIVEN, + token_provider: BedrockTokenProvider | None = None, + access_key_id: str | None = None, + secret_access_key: str | None = None, + session_token: str | None = None, + profile: str | None = None, + credential_provider: AwsCredentialsProvider | None = None, +) -> _Provider: + """Configure the standard OpenAI client for Amazon Bedrock Mantle.""" + + normalized_region = _normalize_optional_string(region) + if region is not None and normalized_region is None: + raise OpenAIError("The Bedrock AWS `region` must not be empty.") + + region_source: Literal["explicit", "environment"] | None = None + if normalized_region is not None: + region_source = "explicit" + else: + normalized_region = _normalize_optional_string( + os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") + ) + if normalized_region is not None: + region_source = "environment" + + configured_base_url: httpx.URL | None + if isinstance(base_url, NotGiven): + environment_base_url = _normalize_optional_string(os.environ.get("AWS_BEDROCK_BASE_URL")) + configured_base_url = _normalize_base_url(environment_base_url) if environment_base_url else None + elif base_url is None: + configured_base_url = None + else: + if isinstance(base_url, str) and not base_url.strip(): + raise OpenAIError("The Bedrock `base_url` must not be empty.") + configured_base_url = _normalize_base_url(base_url) + + normalized_profile = _normalize_optional_string(profile) + if profile is not None and normalized_profile is None: + raise OpenAIError("The Bedrock AWS `profile` must not be empty.") + + if (access_key_id is None) != (secret_access_key is None) or (session_token is not None and access_key_id is None): + raise OpenAIError( + "Static AWS credentials require both `access_key_id` and `secret_access_key`. " + "A `session_token` may only be used with both." + ) + if access_key_id is not None and (not access_key_id.strip() or not cast(str, secret_access_key).strip()): + raise OpenAIError("Static AWS credentials require non-empty `access_key_id` and `secret_access_key` values.") + if session_token is not None and not session_token.strip(): + raise OpenAIError("A static AWS `session_token` must not be empty when provided.") + + explicit_api_key = not isinstance(api_key, NotGiven) and api_key is not None + if explicit_api_key and (not isinstance(api_key, str) or not api_key.strip()): + raise OpenAIError("The Bedrock bearer credential must not be empty.") + if explicit_api_key and token_provider is not None: + raise OpenAIError("The `api_key` and `token_provider` options are mutually exclusive. Configure only one.") + + explicit_bearer = explicit_api_key or token_provider is not None + aws_modes = sum( + ( + access_key_id is not None, + normalized_profile is not None, + credential_provider is not None, + ) + ) + if aws_modes > 1: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit AWS mode: static credentials, " + "profile, or credential provider." + ) + if explicit_bearer and aws_modes: + raise OpenAIError( + "Bedrock authentication is ambiguous. Configure exactly one explicit mode: bearer credential, " + "static AWS credentials, profile, or credential provider." + ) + + skip_environment_bearer = not isinstance(api_key, NotGiven) and api_key is None + use_environment_bearer = ( + not explicit_bearer + and not aws_modes + and not skip_environment_bearer + and bool(os.environ.get("AWS_BEARER_TOKEN_BEDROCK")) + ) + + return _create_provider( + _BedrockProviderDefinition( + configured_region=normalized_region, + region_source=region_source, + configured_base_url=configured_base_url, + api_key=cast("str | None", api_key) if explicit_api_key else None, + token_provider=token_provider, + use_environment_bearer=use_environment_bearer, + profile=normalized_profile, + access_key_id=access_key_id, + secret_access_key=secret_access_key, + session_token=session_token, + credential_provider=credential_provider, + ) + ) + + +__all__ = ["bedrock", "BedrockTokenProvider", "AwsCredentialsProvider"] diff --git a/tests/fixtures/bedrock/v1/sigv4.json b/tests/fixtures/bedrock/v1/sigv4.json new file mode 100644 index 0000000000..441c634999 --- /dev/null +++ b/tests/fixtures/bedrock/v1/sigv4.json @@ -0,0 +1,23 @@ +{ + "$comment": "Uses AWS SigV4 documentation test credentials. These are public examples, not real AWS credentials.", + "signingDate": "2025-01-02T03:04:05.000Z", + "region": "us-east-1", + "service": "bedrock-mantle", + "credentials": { + "accessKeyId": "AKIDEXAMPLE", + "secretAccessKey": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", + "sessionToken": "fixture-session-token" + }, + "request": { + "method": "POST", + "url": "https://bedrock-mantle.us-east-1.api.aws/openai/v1/responses", + "body": "{\"model\":\"gpt-4o\",\"input\":\"hello\"}", + "contentType": "application/json" + }, + "expected": { + "date": "20250102T030405Z", + "payloadHash": "50329e51ad520f21b77bad0b01999930ff556cd1bf18434701251ba6c9f877bc", + "canonicalRequestHash": "1b69b17ef7548a7bf16a6ee749acfd44b7793e04216345dddd6cfaf4c01bfde5", + "authorization": "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20250102/us-east-1/bedrock-mantle/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=dc20c8fbe516daf0ccae7e5b7a78fc2936870413a9f1af6e1b2d44b970ce411f" + } +} diff --git a/tests/fixtures/bedrock_auth/v1/cases.json b/tests/fixtures/bedrock_auth/v1/cases.json new file mode 100644 index 0000000000..602ab703c3 --- /dev/null +++ b/tests/fixtures/bedrock_auth/v1/cases.json @@ -0,0 +1,292 @@ +{ + "schema_version": 1, + "suite": "aws-bedrock-auth", + "cases": [ + { + "id": "auth.explicit-bearer", + "kind": "auth_selection", + "given": { + "explicit": { + "bearer": "explicit-bearer-token" + }, + "environment": { + "AWS_BEARER_TOKEN_BEDROCK": "environment-bearer-token" + }, + "default_chain_available": true + }, + "expected": { + "auth_mode": "bearer", + "auth_source": "explicit" + } + }, + { + "id": "auth.explicit-aws-over-environment-bearer", + "kind": "auth_selection", + "given": { + "explicit": { + "aws": { + "access_key_id": "AKIDEXAMPLE", + "secret_access_key": "fixture-secret-access-key" + } + }, + "environment": { + "AWS_BEARER_TOKEN_BEDROCK": "environment-bearer-token" + }, + "default_chain_available": true + }, + "expected": { + "auth_mode": "sigv4", + "auth_source": "static" + } + }, + { + "id": "auth.explicit-profile-over-environment-bearer", + "kind": "auth_selection", + "given": { + "explicit": { + "profile": "fixture-profile" + }, + "environment": { + "AWS_BEARER_TOKEN_BEDROCK": "environment-bearer-token" + }, + "default_chain_available": true + }, + "expected": { + "auth_mode": "sigv4", + "auth_source": "profile" + } + }, + { + "id": "auth.environment-bearer-over-default-chain", + "kind": "auth_selection", + "given": { + "explicit": {}, + "environment": { + "AWS_BEARER_TOKEN_BEDROCK": "environment-bearer-token" + }, + "default_chain_available": true + }, + "expected": { + "auth_mode": "bearer", + "auth_source": "environment" + } + }, + { + "id": "auth.default-chain", + "kind": "auth_selection", + "given": { + "explicit": {}, + "environment": {}, + "default_chain_available": true + }, + "expected": { + "auth_mode": "sigv4", + "auth_source": "default" + } + }, + { + "id": "auth.empty-environment-bearer-is-absent", + "kind": "auth_selection", + "given": { + "explicit": {}, + "environment": { + "AWS_BEARER_TOKEN_BEDROCK": "" + }, + "default_chain_available": true + }, + "expected": { + "auth_mode": "sigv4", + "auth_source": "default" + } + }, + { + "id": "auth.conflicting-explicit-modes", + "kind": "auth_selection", + "given": { + "explicit": { + "bearer": "explicit-bearer-token", + "aws": { + "access_key_id": "AKIDEXAMPLE", + "secret_access_key": "fixture-secret-access-key" + } + }, + "environment": {}, + "default_chain_available": true + }, + "expected": { + "error": "bedrock_conflicting_auth" + } + }, + { + "id": "sigv4.responses.static-credentials", + "kind": "sigv4", + "given": { + "credentials": { + "access_key_id": "AKIDEXAMPLE", + "secret_access_key": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" + }, + "signing": { + "service": "bedrock-mantle", + "region": "us-east-1", + "timestamp": "2026-06-01T12:34:56Z" + }, + "request": { + "method": "POST", + "url": "https://bedrock-mantle.us-east-1.api.aws/openai/v1/responses", + "headers": { + "content-length": "47", + "content-type": "application/json", + "host": "bedrock-mantle.us-east-1.api.aws" + }, + "body_base64": "eyJpbnB1dCI6ImhlbGxvIiwibW9kZWwiOiJvcGVuYWkuZ3B0LW9zcy0xMjBiIn0=" + } + }, + "expected": { + "payload_sha256": "9d161ca213ef31dd5b8f01f52294bcd5057bc778cf6ee7719e67b7ff43ef1022", + "canonical_request_sha256": "c8da8b3104c310e27f93e2b58560f59a9397cbca97d954f50654e9305dc1c9ab", + "headers": { + "x-amz-content-sha256": "9d161ca213ef31dd5b8f01f52294bcd5057bc778cf6ee7719e67b7ff43ef1022", + "x-amz-date": "20260601T123456Z", + "authorization": "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20260601/us-east-1/bedrock-mantle/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date, Signature=bba77a80c7b39301d1aa29db9bc2f2b349deb932ae2dc5f800cec17baaf0f5de" + } + } + }, + { + "id": "sigv4.responses.temporary-credentials", + "kind": "sigv4", + "given": { + "credentials": { + "access_key_id": "AKIDEXAMPLE", + "secret_access_key": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", + "session_token": "fixture-session-token" + }, + "signing": { + "service": "bedrock-mantle", + "region": "us-east-1", + "timestamp": "2026-06-01T12:34:56Z" + }, + "request": { + "method": "POST", + "url": "https://bedrock-mantle.us-east-1.api.aws/openai/v1/responses", + "headers": { + "content-length": "47", + "content-type": "application/json", + "host": "bedrock-mantle.us-east-1.api.aws" + }, + "body_base64": "eyJpbnB1dCI6ImhlbGxvIiwibW9kZWwiOiJvcGVuYWkuZ3B0LW9zcy0xMjBiIn0=" + } + }, + "expected": { + "payload_sha256": "9d161ca213ef31dd5b8f01f52294bcd5057bc778cf6ee7719e67b7ff43ef1022", + "canonical_request_sha256": "fe294f3ccfa69899816c7e59516dbfae6bf89959da4d64296aa7e0fcb1f9d130", + "headers": { + "x-amz-content-sha256": "9d161ca213ef31dd5b8f01f52294bcd5057bc778cf6ee7719e67b7ff43ef1022", + "x-amz-date": "20260601T123456Z", + "x-amz-security-token": "fixture-session-token", + "authorization": "AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20260601/us-east-1/bedrock-mantle/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=693a4fef0373c0929f1f71d0da29c748f6bf42e75059648ceb97d52a204fb37c" + } + } + }, + { + "id": "retry.fresh-credentials-and-time", + "kind": "retry_signing", + "given": { + "response_statuses": [ + 500, + 200 + ], + "timestamps": [ + "2026-06-01T12:34:56Z", + "2026-06-01T12:35:01Z" + ], + "access_key_ids": [ + "FIRSTACCESSKEY", + "SECONDACCESSKEY" + ], + "body_base64": "eyJpbnB1dCI6ImhlbGxvIiwibW9kZWwiOiJvcGVuYWkuZ3B0LW9zcy0xMjBiIn0=" + }, + "expected": { + "attempts": 2, + "credential_provider_calls": 2, + "x_amz_dates": [ + "20260601T123456Z", + "20260601T123501Z" + ], + "body_sha256": [ + "9d161ca213ef31dd5b8f01f52294bcd5057bc778cf6ee7719e67b7ff43ef1022", + "9d161ca213ef31dd5b8f01f52294bcd5057bc778cf6ee7719e67b7ff43ef1022" + ] + } + }, + { + "id": "body.replayable-bytes", + "kind": "body_replay", + "given": { + "body_kind": "bytes", + "body_base64": "eyJpbnB1dCI6ImhlbGxvIiwibW9kZWwiOiJvcGVuYWkuZ3B0LW9zcy0xMjBiIn0=", + "response_statuses": [ + 500, + 200 + ] + }, + "expected": { + "attempts": 2, + "result": "replayed" + } + }, + { + "id": "body.non-replayable-stream-with-retries", + "kind": "body_replay", + "given": { + "body_kind": "one_shot_stream", + "chunks_base64": [ + "Zmlyc3Q=", + "c2Vjb25k" + ], + "max_retries": 1 + }, + "expected": { + "network_attempts": 0, + "result": "bedrock_non_replayable_body" + } + }, + { + "id": "body.non-replayable-stream-without-retries", + "kind": "body_replay", + "given": { + "body_kind": "one_shot_stream", + "chunks_base64": [ + "Zmlyc3Q=", + "c2Vjb25k" + ], + "max_retries": 0 + }, + "expected": { + "network_attempts": 0, + "result": "bedrock_non_replayable_body" + } + }, + { + "id": "redaction.sigv4-and-bearer", + "kind": "redaction", + "given": { + "headers": { + "authorization": "AWS4-HMAC-SHA256 fixture-authorization", + "x-amz-security-token": "fixture-session-token", + "x-request-id": "req_fixture" + } + }, + "expected": { + "headers": { + "authorization": "", + "x-amz-security-token": "", + "x-request-id": "req_fixture" + }, + "forbidden_substrings": [ + "fixture-authorization", + "fixture-session-token" + ] + } + } + ] +} diff --git a/tests/fixtures/bedrock_auth/v1/schema.json b/tests/fixtures/bedrock_auth/v1/schema.json new file mode 100644 index 0000000000..52940a6df5 --- /dev/null +++ b/tests/fixtures/bedrock_auth/v1/schema.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://openai.com/sdk-fixtures/bedrock-auth/v1/schema.json", + "title": "OpenAI SDK AWS Bedrock authentication conformance fixtures", + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "suite", "cases"], + "properties": { + "schema_version": { "const": 1 }, + "suite": { "const": "aws-bedrock-auth" }, + "cases": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/case" } + } + }, + "$defs": { + "case": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "given", "expected"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "kind": { + "enum": ["auth_selection", "sigv4", "retry_signing", "body_replay", "redaction"] + }, + "given": { "type": "object" }, + "expected": { "type": "object" } + }, + "allOf": [ + { + "if": { "properties": { "kind": { "const": "auth_selection" } } }, + "then": { + "properties": { + "given": { "required": ["explicit", "environment", "default_chain_available"] } + } + } + }, + { + "if": { "properties": { "kind": { "const": "sigv4" } } }, + "then": { + "properties": { + "given": { "required": ["credentials", "signing", "request"] }, + "expected": { "required": ["payload_sha256", "canonical_request_sha256", "headers"] } + } + } + }, + { + "if": { "properties": { "kind": { "const": "retry_signing" } } }, + "then": { + "properties": { + "given": { "required": ["response_statuses", "timestamps", "access_key_ids", "body_base64"] }, + "expected": { "required": ["attempts", "credential_provider_calls", "x_amz_dates", "body_sha256"] } + } + } + }, + { + "if": { "properties": { "kind": { "const": "body_replay" } } }, + "then": { + "properties": { + "given": { "required": ["body_kind"] }, + "expected": { "required": ["result"] } + } + } + }, + { + "if": { "properties": { "kind": { "const": "redaction" } } }, + "then": { + "properties": { + "given": { "required": ["headers"] }, + "expected": { "required": ["headers", "forbidden_substrings"] } + } + } + } + ] + } + } +} diff --git a/tests/lib/bedrock_live.py b/tests/lib/bedrock_live.py new file mode 100644 index 0000000000..95c1a71c1d --- /dev/null +++ b/tests/lib/bedrock_live.py @@ -0,0 +1,100 @@ +"""Opt-in live test for the Amazon Bedrock provider. + +This file is intentionally named ``bedrock_live.py`` so the standard pytest +suite does not collect it. Run it explicitly with: + + rye run pytest -q -s tests/lib/bedrock_live.py + +The test loads ``.env`` from the repository root when it exists. Set +``BEDROCK_LIVE_ENV_FILE`` to load a different file. Existing environment +variables take precedence over values from the file. + +Useful environment variables: + +- ``BEDROCK_LIVE_MODEL`` (defaults to ``openai.gpt-oss-120b``) +- ``BEDROCK_LIVE_REGION`` (otherwise uses the normal AWS region chain) +- ``BEDROCK_LIVE_PROFILE`` (otherwise uses the normal AWS credential chain) +- ``AWS_BEDROCK_BASE_URL`` (required; GPT-OSS uses the ``/v1`` endpoint) + +The normal AWS environment variables, shared config, credential-process, SSO, +and workload credential sources are resolved by botocore. The test explicitly +disables the ``AWS_BEARER_TOKEN_BEDROCK`` fallback so a passing run proves that +AWS credentials and SigV4 signing worked. +""" + +from __future__ import annotations + +import os +import re +from pathlib import Path + +from openai import OpenAI +from openai.providers import bedrock + +_REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +_ENV_NAME = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") + + +def _load_env_file() -> None: + configured_path = os.environ.get("BEDROCK_LIVE_ENV_FILE") + path = Path(configured_path).expanduser() if configured_path else _REPOSITORY_ROOT / ".env" + if not path.exists(): + if configured_path: + raise RuntimeError(f"BEDROCK_LIVE_ENV_FILE does not exist: {path}") + return + + for line_number, raw_line in enumerate(path.read_text().splitlines(), start=1): + line = raw_line.strip() + if not line or line.startswith("#"): + continue + if line.startswith("export "): + line = line.removeprefix("export ").lstrip() + + name, separator, raw_value = line.partition("=") + name = name.strip() + if not separator or _ENV_NAME.fullmatch(name) is None: + raise RuntimeError(f"Invalid environment assignment at {path}:{line_number}") + + value = raw_value.strip() + if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}: + value = value[1:-1] + elif " #" in value: + value = value.split(" #", 1)[0].rstrip() + + os.environ.setdefault(name, value) + + +_load_env_file() + + +def test_bedrock_live_response() -> None: + model = os.environ.get("BEDROCK_LIVE_MODEL") or "openai.gpt-oss-120b" + region = os.environ.get("BEDROCK_LIVE_REGION") or None + profile = os.environ.get("BEDROCK_LIVE_PROFILE") or None + base_url = os.environ.get("AWS_BEDROCK_BASE_URL") or None + if base_url is None: + raise RuntimeError( + "Set AWS_BEDROCK_BASE_URL to the Bedrock GPT-OSS endpoint, for example " + "https://bedrock-mantle.us-west-2.api.aws/v1." + ) + + with OpenAI( + provider=bedrock( + region=region, + profile=profile, + base_url=base_url, + api_key=None, + ), + timeout=60, + max_retries=2, + ) as client: + response = client.responses.create( + model=model, + input="Reply with exactly: bedrock live test ok", + store=False, + ) + + output_text = response.output_text.strip() + assert output_text, f"Bedrock returned no output text for response {response.id}" + assert "bedrock live test ok" in output_text.lower() + print(f"Bedrock live response {response.id}: {output_text}") diff --git a/tests/lib/test_bedrock.py b/tests/lib/test_bedrock.py index dab9abd1cf..14c91c0661 100644 --- a/tests/lib/test_bedrock.py +++ b/tests/lib/test_bedrock.py @@ -2,12 +2,14 @@ import json from typing import Any, Union, Protocol, cast +from pathlib import Path import httpx import pytest from httpx import URL from respx import MockRouter +import openai.lib._bedrock_auth as bedrock_auth_module from openai import OpenAIError, NotFoundError from tests.utils import update_env from openai._types import Omit @@ -76,6 +78,13 @@ class MockRequestCall(Protocol): request: httpx.Request +class MockAwsCredentials: + def __init__(self, access_key: str, secret_key: str, token: str | None = None) -> None: + self.access_key = access_key + self.secret_key = secret_key + self.token = token + + def make_sync_client(**kwargs: Any) -> BedrockOpenAI: return BedrockOpenAI(http_client=httpx.Client(trust_env=False), **kwargs) @@ -123,6 +132,75 @@ def test_bedrock_config_precedence(client_cls: type[Client]) -> None: assert client.api_key == "explicit token" +@pytest.mark.respx() +def test_env_bearer_does_not_require_botocore(monkeypatch: pytest.MonkeyPatch, respx_mock: MockRouter) -> None: + def load_botocore() -> None: + raise AssertionError("bearer authentication must not import botocore") + + monkeypatch.setattr(bedrock_auth_module, "_load_botocore", load_botocore) + respx_mock.post("https://example.com/openai/v1/responses").mock( + return_value=httpx.Response(200, json=RESPONSE_BODY) + ) + with update_env( + AWS_BEDROCK_BASE_URL="https://example.com/openai/v1", + AWS_BEARER_TOKEN_BEDROCK="env token", + ): + client = make_sync_client() + + client.responses.create(model="gpt-4o", input="hello") + + request = cast("list[MockRequestCall]", respx_mock.calls)[0].request + assert request.headers["Authorization"] == "Bearer env token" + + +def test_empty_env_bearer_without_botocore_uses_aws_credentials(monkeypatch: pytest.MonkeyPatch) -> None: + def load_botocore() -> None: + raise OpenAIError( + "Bedrock AWS authentication requires optional AWS dependencies. " + "Install them with `pip install openai[bedrock]` and try again." + ) + + monkeypatch.setattr(bedrock_auth_module, "_load_botocore", load_botocore) + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_BEARER_TOKEN_BEDROCK="", AWS_REGION="us-east-1"): + client = make_sync_client() + with pytest.raises(OpenAIError, match="requires optional AWS dependencies"): + client.get("/models", cast_to=httpx.Response) + + +@pytest.mark.respx() +def test_env_bearer_does_not_use_botocore_bearer_auth(monkeypatch: pytest.MonkeyPatch, respx_mock: MockRouter) -> None: + auth_module = pytest.importorskip("botocore.auth") + calls = 0 + real_add_auth = auth_module.BearerAuth.add_auth + + def add_auth(auth: object, request: object) -> None: + nonlocal calls + calls += 1 + real_add_auth(auth, request) + + monkeypatch.setattr(auth_module.BearerAuth, "add_auth", add_auth) + respx_mock.post("https://example.com/openai/v1/responses").mock( + return_value=httpx.Response(200, json=RESPONSE_BODY) + ) + with update_env(AWS_BEARER_TOKEN_BEDROCK="env token"): + client = make_sync_client(base_url="https://example.com/openai/v1") + + client.responses.create(model="gpt-4o", input="hello") + + request = cast("list[MockRequestCall]", respx_mock.calls)[0].request + assert request.headers["Authorization"] == "Bearer env token" + assert calls == 0 + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_empty_env_bearer_falls_back_to_aws_credentials(client_cls: type[Client]) -> None: + with update_env(AWS_BEARER_TOKEN_BEDROCK="", AWS_REGION="us-east-1"): + client = make_sync_client() if client_cls is BedrockOpenAI else make_async_client() + + assert client.api_key == "" + assert client._uses_aws_auth() + + @pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) def test_bedrock_region_precedence(client_cls: type[Client]) -> None: with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION="us-east-1", AWS_DEFAULT_REGION="us-west-2"): @@ -145,6 +223,26 @@ def test_bedrock_region_precedence(client_cls: type[Client]) -> None: assert default_region_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_aws_profile_supplies_region(client_cls: type[Client], tmp_path: Path) -> None: + config_path = tmp_path / "config" + config_path.write_text("[profile production]\nregion = eu-central-1\n") + with update_env( + AWS_CONFIG_FILE=str(config_path), + AWS_BEDROCK_BASE_URL=Omit(), + AWS_REGION=Omit(), + AWS_DEFAULT_REGION=Omit(), + ): + client = ( + make_sync_client(aws_profile="production") + if client_cls is BedrockOpenAI + else make_async_client(aws_profile="production") + ) + + assert client.aws_region == "eu-central-1" + assert client.base_url == URL("https://bedrock-mantle.eu-central-1.api.aws/openai/v1/") + + @pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) def test_normalizes_responses_url(client_cls: type[Client]) -> None: client = ( @@ -159,7 +257,7 @@ def test_normalizes_responses_url(client_cls: type[Client]) -> None: @pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) def test_requires_endpoint_configuration(client_cls: type[Client]) -> None: with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): - with pytest.raises(OpenAIError, match="Must provide one of the `base_url` or `aws_region`"): + with pytest.raises(OpenAIError, match="Bedrock requires an AWS region"): client_cls(api_key="token") @@ -169,14 +267,17 @@ def test_does_not_use_openai_api_key(client_cls: type[Client]) -> None: OPENAI_API_KEY="openai token", AWS_BEARER_TOKEN_BEDROCK=Omit(), AWS_BEDROCK_BASE_URL="https://example.com/openai/v1", + AWS_REGION="us-east-1", ): - with pytest.raises(OpenAIError, match="AWS_BEARER_TOKEN_BEDROCK"): - client_cls() + client = make_sync_client() if client_cls is BedrockOpenAI else make_async_client() + + assert client.api_key == "" + assert client._uses_aws_auth() @pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) def test_rejects_static_token_and_provider(client_cls: type[Client]) -> None: - with pytest.raises(OpenAIError, match="mutually exclusive"): + with pytest.raises(OpenAIError, match="authentication is ambiguous"): client_cls( base_url="https://example.com/openai/v1", api_key="token", @@ -184,6 +285,33 @@ def test_rejects_static_token_and_provider(client_cls: type[Client]) -> None: ) +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_rejects_empty_explicit_bearer_token(client_cls: type[Client]) -> None: + with pytest.raises(OpenAIError, match="must not be empty"): + client_cls(base_url="https://example.com/openai/v1", api_key="") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_rejects_bearer_and_aws_credentials(client_cls: type[Client]) -> None: + with pytest.raises(OpenAIError, match="authentication is ambiguous"): + client_cls( + base_url="https://example.com/openai/v1", + api_key="token", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ) + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_rejects_partial_explicit_aws_credentials(client_cls: type[Client]) -> None: + with pytest.raises(OpenAIError, match="require both"): + client_cls( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_access_key_id="access key", + ) + + @pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) def test_requires_refreshable_tokens_to_use_provider_option(client_cls: type[Client]) -> None: with pytest.raises(OpenAIError, match="bedrock_token_provider"): @@ -240,6 +368,59 @@ async def test_token_provider_refresh_async(respx_mock: MockRouter) -> None: assert calls[1].request.headers["Authorization"] == "Bearer second" +@pytest.mark.respx() +def test_explicit_aws_credentials_override_ambient_bearer(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/responses").mock( + return_value=httpx.Response(200, json=RESPONSE_BODY) + ) + with update_env(AWS_BEARER_TOKEN_BEDROCK="ambient token"): + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + aws_session_token="session token", + http_client=httpx.Client(trust_env=False), + ) + + client.responses.create(model="gpt-4o", input="hello") + + request = cast("list[MockRequestCall]", respx_mock.calls)[0].request + assert request.headers["Authorization"].startswith("AWS4-HMAC-SHA256 Credential=access key/") + assert request.headers["X-Amz-Security-Token"] == "session token" + + +@pytest.mark.respx() +def test_aws_credentials_provider_refreshes_before_retries(respx_mock: MockRouter) -> None: + respx_mock.post("https://example.com/openai/v1/responses").mock( + side_effect=[ + httpx.Response(500, json={"error": "server error"}), + httpx.Response(200, json=RESPONSE_BODY), + ] + ) + credentials = iter( + [ + MockAwsCredentials("first access key", "first secret", "first session token"), + MockAwsCredentials("second access key", "second secret", "second session token"), + ] + ) + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_credentials_provider=lambda: next(credentials), + http_client=httpx.Client(trust_env=False), + max_retries=1, + ) + + client.responses.create(model="gpt-4o", input="hello") + + calls = cast("list[MockRequestCall]", respx_mock.calls) + assert "Credential=first access key/" in calls[0].request.headers["Authorization"] + assert calls[0].request.headers["X-Amz-Security-Token"] == "first session token" + assert "Credential=second access key/" in calls[1].request.headers["Authorization"] + assert calls[1].request.headers["X-Amz-Security-Token"] == "second session token" + + def test_preserves_token_provider_across_with_options() -> None: client = BedrockOpenAI( base_url="https://example.com/openai/v1", @@ -252,6 +433,524 @@ def test_preserves_token_provider_across_with_options() -> None: assert copied_client._refresh_api_key() == "provider token" +def test_preserves_environment_bearer_across_with_options() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"): + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + + with update_env(AWS_BEARER_TOKEN_BEDROCK="second token"): + copied_client = client.with_options(timeout=1) + copied_client.get("/models", cast_to=httpx.Response) + + assert copied_client.api_key == "first token" + assert requests[0].headers["Authorization"] == "Bearer first token" + + +def test_environment_bearer_routing_copy_remains_mutable() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"): + client = BedrockOpenAI( + aws_region="us-east-1", + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + copied_client = client.with_options(aws_region="us-west-2") + copied_client.api_key = "second token" + copied_client.get("/models", cast_to=httpx.Response) + + assert copied_client.api_key == "second token" + assert requests[0].headers["Authorization"] == "Bearer second token" + + +def test_legacy_api_key_mutation_updates_requests_and_copies() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + api_key="first token", + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + client.api_key = "second token" + client.get("/models", cast_to=httpx.Response) + copied_client = client.with_options(timeout=1) + copied_client.get("/models", cast_to=httpx.Response) + client.api_key = "first token" + reverted_client = client.with_options(timeout=2) + reverted_client.get("/models", cast_to=httpx.Response) + + assert copied_client.api_key == "second token" + assert reverted_client.api_key == "first token" + assert [request.headers["Authorization"] for request in requests] == [ + "Bearer second token", + "Bearer second token", + "Bearer first token", + ] + + +def test_legacy_api_key_mutation_switches_aws_client_to_bearer() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + client.api_key = "bearer token" + client.get("/models", cast_to=httpx.Response, options={"follow_redirects": True}) + + assert requests[0].headers["Authorization"] == "Bearer bearer token" + + +def test_explicit_aws_copy_override_wins_over_mutated_api_key() -> None: + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + api_key="first token", + http_client=httpx.Client(trust_env=False), + ) + client.api_key = "second token" + + copied_client = client.with_options( + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ) + + assert copied_client._uses_aws_auth() + assert copied_client.api_key == "" + + +def test_clearing_legacy_bearer_does_not_switch_to_aws_authentication() -> None: + network_calls = 0 + + def handler(request: httpx.Request) -> httpx.Response: + nonlocal network_calls + network_calls += 1 + return httpx.Response(200, request=request) + + with update_env(AWS_ACCESS_KEY_ID="access key", AWS_SECRET_ACCESS_KEY="secret key"): + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + api_key="bearer token", + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + client.api_key = None # type: ignore[assignment] + with pytest.raises(OpenAIError, match="bearer credential must not be empty"): + client.get("/models", cast_to=httpx.Response) + + assert network_calls == 0 + + +def test_legacy_state_repr_does_not_expose_credentials() -> None: + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_access_key_id="secret access key id", + aws_secret_access_key="secret access key", + aws_session_token="secret session token", + http_client=httpx.Client(trust_env=False), + ) + + assert "secret" not in repr(client._bedrock_state) + + bearer_client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + api_key="secret bearer token", + http_client=httpx.Client(trust_env=False), + ) + assert "secret bearer token" not in repr(bearer_client._bedrock_runtime_signature) + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_direct_routing_mutations_survive_clone(client_cls: type[Client]) -> None: + client = ( + make_sync_client(base_url="https://first.example/openai/v1", aws_region="us-east-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client( + base_url="https://first.example/openai/v1", + aws_region="us-east-1", + api_key="token", + ) + ) + client.base_url = "https://second.example/openai/v1" + client.aws_region = "us-west-2" + + copied_client = client.with_options(timeout=1) + + assert copied_client.base_url == URL("https://second.example/openai/v1/") + assert copied_client.aws_region == "us-west-2" + assert not copied_client._bedrock_state.uses_region_derived_base_url + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_direct_region_mutation_survives_clone(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): + client = ( + make_sync_client(aws_region="us-east-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(aws_region="us-east-1", api_key="token") + ) + client.aws_region = "us-west-2" + copied_client = client.with_options(timeout=1) + + assert copied_client.aws_region == "us-west-2" + assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_direct_base_url_mutation_survives_auth_override(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): + client = ( + make_sync_client( + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ) + if client_cls is BedrockOpenAI + else make_async_client( + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ) + ) + client.base_url = "https://custom.example/openai/v1" + copied_client = client.with_options(api_key="bearer token") + + assert copied_client.base_url == URL("https://custom.example/openai/v1/") + assert not copied_client._uses_aws_auth() + + +def test_preserves_aws_credentials_across_with_options() -> None: + client = BedrockOpenAI( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + http_client=httpx.Client(trust_env=False), + ) + + copied_client = client.with_options(timeout=1) + + assert copied_client._uses_aws_auth() + assert copied_client._bedrock_state.aws_access_key_id == "access key" + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_preserves_default_chain_mode_across_with_options(client_cls: type[Client]) -> None: + with update_env(AWS_BEARER_TOKEN_BEDROCK=Omit(), AWS_REGION="us-east-1"): + client = make_sync_client() if client_cls is BedrockOpenAI else make_async_client() + + with update_env(AWS_BEARER_TOKEN_BEDROCK="late bearer", AWS_REGION="us-east-1"): + copied_client = client.with_options(timeout=1) + + assert copied_client._uses_aws_auth() + assert copied_client._bedrock_state.aws_profile is None + assert copied_client._bedrock_state.aws_access_key_id is None + assert copied_client._bedrock_state.aws_credentials_provider is None + assert copied_client.api_key == "" + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_preserves_region_derived_url_provenance_across_multiple_copies(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): + client = ( + make_sync_client(aws_region="us-east-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(aws_region="us-east-1", api_key="token") + ) + copied_client = client.with_options(timeout=1).with_options(aws_region="eu-west-1") + + assert copied_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_preserves_region_derived_url_after_auth_override(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): + client = ( + make_sync_client(aws_region="us-east-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(aws_region="us-east-1", api_key="token") + ) + copied_client = client.with_options( + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ).with_options(aws_region="us-west-2") + + assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_blank_base_url_restores_region_derived_url_provenance(client_cls: type[Client]) -> None: + with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): + client = ( + make_sync_client(aws_region="us-east-1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(aws_region="us-east-1", api_key="token") + ) + copied_client = client.with_options(base_url="").with_options(aws_region="eu-west-1") + + assert copied_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_with_options_replaces_the_aws_credential_source(client_cls: type[Client], tmp_path: Path) -> None: + config_path = tmp_path / "config" + config_path.write_text("[profile other-profile]\nregion = us-east-1\n") + explicit_credentials_client = ( + make_sync_client( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ) + if client_cls is BedrockOpenAI + else make_async_client( + base_url="https://example.com/openai/v1", + aws_region="us-east-1", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ) + ) + with update_env(AWS_CONFIG_FILE=str(config_path)): + profile_client = explicit_credentials_client.with_options(aws_profile="other-profile") + + assert profile_client._bedrock_state.aws_profile == "other-profile" + assert profile_client._bedrock_state.aws_access_key_id is None + assert profile_client._bedrock_state.aws_secret_access_key is None + + explicit_credentials_client = profile_client.with_options( + aws_access_key_id="replacement access key", + aws_secret_access_key="replacement secret key", + ) + + assert explicit_credentials_client._bedrock_state.aws_profile is None + assert explicit_credentials_client._bedrock_state.aws_access_key_id == "replacement access key" + assert explicit_credentials_client._bedrock_state.aws_secret_access_key == "replacement secret key" + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_with_options_replacing_profile_re_resolves_profile_region(client_cls: type[Client], tmp_path: Path) -> None: + config_path = tmp_path / "config" + config_path.write_text("[profile east]\nregion = us-east-1\n[profile west]\nregion = us-west-2\n") + + with update_env( + AWS_CONFIG_FILE=str(config_path), + AWS_BEARER_TOKEN_BEDROCK=Omit(), + AWS_BEDROCK_BASE_URL=Omit(), + AWS_REGION=Omit(), + AWS_DEFAULT_REGION=Omit(), + ): + client = ( + make_sync_client(aws_profile="east") + if client_cls is BedrockOpenAI + else make_async_client(aws_profile="east") + ) + + with update_env( + AWS_CONFIG_FILE=str(config_path), + AWS_BEARER_TOKEN_BEDROCK=Omit(), + AWS_BEDROCK_BASE_URL="https://late-environment.example.com/v1", + AWS_REGION=Omit(), + AWS_DEFAULT_REGION=Omit(), + ): + copied_client = client.with_options(aws_profile="west") + + assert copied_client.aws_region == "us-west-2" + assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + assert copied_client._bedrock_state.aws_profile == "west" + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +@pytest.mark.parametrize( + ("copy_kwargs", "uses_aws_auth"), + [ + ({"api_key": "bearer token"}, False), + ( + { + "aws_access_key_id": "access key", + "aws_secret_access_key": "secret key", + }, + True, + ), + ], +) +def test_profile_derived_region_survives_auth_override( + client_cls: type[Client], + copy_kwargs: dict[str, Any], + uses_aws_auth: bool, + tmp_path: Path, +) -> None: + config_path = tmp_path / "config" + config_path.write_text("[profile west]\nregion = us-west-2\n") + + with update_env( + AWS_CONFIG_FILE=str(config_path), + AWS_BEARER_TOKEN_BEDROCK=Omit(), + AWS_BEDROCK_BASE_URL=Omit(), + AWS_REGION=Omit(), + AWS_DEFAULT_REGION=Omit(), + ): + client = ( + make_sync_client(aws_profile="west") + if client_cls is BedrockOpenAI + else make_async_client(aws_profile="west") + ) + copied_client = client.with_options(**copy_kwargs) + + assert copied_client.aws_region == "us-west-2" + assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + assert copied_client._uses_aws_auth() is uses_aws_auth + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +@pytest.mark.parametrize( + ("aws_profile", "config_contents"), + [ + ("west", "[profile west]\nregion = us-west-2\n[profile east]\nregion = us-east-1\n"), + (None, "[default]\nregion = us-west-2\n[profile east]\nregion = us-east-1\n"), + ], + ids=["named-profile", "default-profile"], +) +def test_profile_derived_region_survives_credential_override_with_custom_base_url( + client_cls: type[Client], aws_profile: str | None, config_contents: str, tmp_path: Path +) -> None: + config_path = tmp_path / "config" + config_path.write_text(config_contents) + + with update_env( + AWS_CONFIG_FILE=str(config_path), + AWS_PROFILE=Omit(), + AWS_BEARER_TOKEN_BEDROCK=Omit(), + AWS_REGION=Omit(), + AWS_DEFAULT_REGION=Omit(), + ): + client = ( + make_sync_client(base_url="https://custom.example/openai/v1", aws_profile=aws_profile) + if client_cls is BedrockOpenAI + else make_async_client(base_url="https://custom.example/openai/v1", aws_profile=aws_profile) + ) + assert client.aws_region == "us-west-2" + + client.aws_region = None + client.with_options(timeout=1) + + copied_client = client.with_options( + aws_access_key_id="replacement access key", + aws_secret_access_key="replacement secret key", + ) + profile_client = copied_client.with_options(aws_profile="east") + + assert client.aws_region == "us-west-2" + assert copied_client.aws_region == "us-west-2" + assert copied_client._bedrock_state.aws_region == "us-west-2" + assert copied_client.base_url == URL("https://custom.example/openai/v1/") + assert copied_client._uses_aws_auth() + assert profile_client.aws_region == "us-east-1" + assert profile_client.base_url == URL("https://custom.example/openai/v1/") + + +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_with_options_switching_from_bearer_to_profile_re_resolves_environment_region( + client_cls: type[Client], tmp_path: Path +) -> None: + config_path = tmp_path / "config" + config_path.write_text("[profile west]\nregion = us-west-2\n") + + with update_env( + AWS_CONFIG_FILE=str(config_path), + AWS_BEDROCK_BASE_URL=Omit(), + AWS_REGION="us-east-1", + AWS_DEFAULT_REGION=Omit(), + ): + client = ( + make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token") + ) + + with update_env( + AWS_CONFIG_FILE=str(config_path), + AWS_BEARER_TOKEN_BEDROCK=Omit(), + AWS_BEDROCK_BASE_URL=Omit(), + AWS_REGION=Omit(), + AWS_DEFAULT_REGION=Omit(), + ): + copied_client = client.with_options(aws_profile="west") + + assert copied_client.aws_region == "us-west-2" + assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + + +def test_with_options_supports_subclasses_with_the_previous_constructor_signature() -> None: + class LegacyBedrockOpenAI(BedrockOpenAI): + def __init__( + self, + *, + api_key: str | None = None, + bedrock_token_provider: Any = None, + aws_region: str | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: Any = None, + max_retries: int = 2, + default_headers: Any = None, + default_query: Any = None, + http_client: httpx.Client | None = None, + _enforce_credentials: bool = True, + ) -> None: + super().__init__( + api_key=api_key, + bedrock_token_provider=bedrock_token_provider, + aws_region=aws_region, + organization=organization, + project=project, + webhook_secret=webhook_secret, + base_url=base_url, + websocket_base_url=websocket_base_url, + timeout=timeout, + max_retries=max_retries, + default_headers=default_headers, + default_query=default_query, + http_client=http_client, + _enforce_credentials=_enforce_credentials, + ) + + with update_env(AWS_BEDROCK_BASE_URL=Omit()): + client = LegacyBedrockOpenAI( + api_key="token", + aws_region="us-east-1", + http_client=httpx.Client(trust_env=False), + ) + + copied_client = client.with_options(timeout=1).with_options(aws_region="us-west-2") + + assert isinstance(copied_client, LegacyBedrockOpenAI) + assert copied_client.api_key == "token" + assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/") + + @pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) def test_with_options_api_key_replaces_token_provider(client_cls: type[Client]) -> None: client = ( @@ -272,6 +971,22 @@ def test_with_options_api_key_replaces_token_provider(client_cls: type[Client]) assert copied_client._bedrock_token_provider is None +@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) +def test_with_options_rejects_explicit_bearer_provider_and_aws_credentials(client_cls: type[Client]) -> None: + client = ( + make_sync_client(base_url="https://example.com/openai/v1", api_key="token") + if client_cls is BedrockOpenAI + else make_async_client(base_url="https://example.com/openai/v1", api_key="token") + ) + + with pytest.raises(OpenAIError, match="authentication is ambiguous"): + client.with_options( + bedrock_token_provider=lambda: "provider token", + aws_access_key_id="access key", + aws_secret_access_key="secret key", + ) + + @pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI]) def test_with_options_aws_region_recomputes_region_derived_base_url(client_cls: type[Client]) -> None: with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()): @@ -311,7 +1026,7 @@ def test_with_options_aws_region_keeps_explicit_base_url(client_cls: type[Client def test_rejects_non_bedrock_copy_auth(copy_kwargs: dict[str, Any]) -> None: client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token") - with pytest.raises(OpenAIError, match="only supports Bedrock bearer token authentication"): + with pytest.raises(OpenAIError, match="only supports Bedrock bearer token or AWS credential authentication"): client.with_options(**copy_kwargs) diff --git a/tests/lib/test_bedrock_auth_conformance.py b/tests/lib/test_bedrock_auth_conformance.py new file mode 100644 index 0000000000..109fa07d78 --- /dev/null +++ b/tests/lib/test_bedrock_auth_conformance.py @@ -0,0 +1,438 @@ +from __future__ import annotations + +import json +import base64 +import hashlib +import logging +import threading +from typing import Any, Iterator, AsyncIterator, cast +from pathlib import Path +from datetime import datetime + +import httpx +import pytest +import jsonschema + +from openai import OpenAI, AsyncOpenAI, OpenAIError, APIStatusError +from openai._utils import SensitiveHeadersFilter +from openai.providers import bedrock +from openai.lib.bedrock import BedrockOpenAI +from openai.lib._bedrock_auth import BedrockAwsAuth, BedrockAwsAuthConfig + +FIXTURE_PATH = Path(__file__).parents[1] / "fixtures" / "bedrock_auth" / "v1" / "cases.json" +SCHEMA_PATH = FIXTURE_PATH.with_name("schema.json") +SHARED_SIGV4_FIXTURE_PATH = Path(__file__).parents[1] / "fixtures" / "bedrock" / "v1" / "sigv4.json" +FIXTURES = cast(dict[str, Any], json.loads(FIXTURE_PATH.read_text())) +SCHEMA = cast(dict[str, Any], json.loads(SCHEMA_PATH.read_text())) +SHARED_SIGV4_FIXTURE = cast(dict[str, Any], json.loads(SHARED_SIGV4_FIXTURE_PATH.read_text())) + + +def _cases(kind: str) -> list[dict[str, Any]]: + return [cast(dict[str, Any], case) for case in FIXTURES["cases"] if case["kind"] == kind] + + +def _fixed_datetime(value: str) -> datetime: + return datetime.fromisoformat(value.replace("Z", "+00:00")) + + +def _freeze_botocore_time(monkeypatch: pytest.MonkeyPatch, timestamps: Iterator[datetime]) -> None: + botocore_auth = pytest.importorskip("botocore.auth") + + if hasattr(botocore_auth, "get_current_datetime"): + monkeypatch.setattr(botocore_auth, "get_current_datetime", lambda: next(timestamps)) + return + + class FrozenDatetime: + @classmethod + def utcnow(cls) -> datetime: + return next(timestamps).replace(tzinfo=None) + + monkeypatch.setattr(botocore_auth.datetime, "datetime", FrozenDatetime) + + +def _lower_headers(headers: httpx.Headers | dict[str, str]) -> dict[str, str]: + return {name.lower(): value for name, value in headers.items()} + + +def _canonical_request_sha256(case: dict[str, Any], signed_headers: dict[str, str], payload_hash: str) -> str: + request = case["given"]["request"] + authorization = signed_headers["authorization"] + signed_header_names = authorization.split("SignedHeaders=", 1)[1].split(",", 1)[0].split(";") + canonical_headers = "".join(f"{name}:{' '.join(signed_headers[name].split())}\n" for name in signed_header_names) + url = httpx.URL(request["url"]) + canonical_request = "\n".join( + ( + request["method"], + url.raw_path.split(b"?", 1)[0].decode(), + url.query.decode(), + canonical_headers, + ";".join(signed_header_names), + payload_hash, + ) + ) + return hashlib.sha256(canonical_request.encode()).hexdigest() + + +def test_shared_sigv4_fixture_matches_node(monkeypatch: pytest.MonkeyPatch) -> None: + fixture = SHARED_SIGV4_FIXTURE + credentials = fixture["credentials"] + request = fixture["request"] + body = request["body"].encode() + payload_hash = hashlib.sha256(body).hexdigest() + _freeze_botocore_time(monkeypatch, iter([_fixed_datetime(fixture["signingDate"])])) + + auth = BedrockAwsAuth( + BedrockAwsAuthConfig( + region=fixture["region"], + source="static", + access_key_id=credentials["accessKeyId"], + secret_access_key=credentials["secretAccessKey"], + session_token=credentials["sessionToken"], + ) + ) + signed_headers = _lower_headers( + auth.sign( + method=request["method"], + url=request["url"], + headers={ + "content-type": request["contentType"], + "host": httpx.URL(request["url"]).host, + }, + body=body, + ) + ) + canonical_case = {"given": {"request": request}} + + assert fixture["service"] == "bedrock-mantle" + assert payload_hash == fixture["expected"]["payloadHash"] + assert ( + _canonical_request_sha256(canonical_case, signed_headers, payload_hash) + == fixture["expected"]["canonicalRequestHash"] + ) + assert signed_headers["authorization"] == fixture["expected"]["authorization"] + assert signed_headers["x-amz-date"] == fixture["expected"]["date"] + + +@pytest.mark.parametrize("case", _cases("auth_selection"), ids=lambda case: case["id"]) +def test_auth_selection_fixture(case: dict[str, Any], monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False) + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + for name, value in case["given"]["environment"].items(): + monkeypatch.setenv(name, value) + + explicit = case["given"]["explicit"] + kwargs: dict[str, Any] = { + "aws_region": "us-east-1", + "http_client": httpx.Client(trust_env=False), + "_enforce_credentials": False, + } + if "bearer" in explicit: + kwargs["api_key"] = explicit["bearer"] + if "aws" in explicit: + kwargs["aws_access_key_id"] = explicit["aws"]["access_key_id"] + kwargs["aws_secret_access_key"] = explicit["aws"]["secret_access_key"] + if "profile" in explicit: + kwargs["aws_profile"] = explicit["profile"] + + if case["expected"].get("error") == "bedrock_conflicting_auth": + with pytest.raises(OpenAIError, match="authentication is ambiguous"): + BedrockOpenAI(**kwargs) + kwargs["http_client"].close() + return + + with BedrockOpenAI(**kwargs) as client: + state = client._bedrock_state + if not client._uses_aws_auth(): + mode = "bearer" + source = "explicit" if state.explicit_api_key is not None else "environment" + else: + mode = "sigv4" + if state.aws_access_key_id is not None: + source = "static" + elif state.aws_profile is not None: + source = "profile" + elif state.aws_credentials_provider is not None: + source = "provider" + else: + source = "default" + + assert mode == case["expected"]["auth_mode"] + assert source == case["expected"]["auth_source"] + + +@pytest.mark.parametrize("case", _cases("sigv4"), ids=lambda case: case["id"]) +def test_sigv4_fixture(case: dict[str, Any], monkeypatch: pytest.MonkeyPatch) -> None: + credentials = case["given"]["credentials"] + signing = case["given"]["signing"] + request = case["given"]["request"] + body = base64.b64decode(request["body_base64"]) + payload_hash = hashlib.sha256(body).hexdigest() + + auth = BedrockAwsAuth( + BedrockAwsAuthConfig( + region=signing["region"], + source="static", + access_key_id=credentials["access_key_id"], + secret_access_key=credentials["secret_access_key"], + session_token=credentials.get("session_token"), + ) + ) + _freeze_botocore_time(monkeypatch, iter([_fixed_datetime(signing["timestamp"])])) + + signed_headers = _lower_headers( + auth.sign( + method=request["method"], + url=request["url"], + headers=request["headers"], + body=body, + ) + ) + + assert signing["service"] == "bedrock-mantle" + assert payload_hash == case["expected"]["payload_sha256"] + assert _canonical_request_sha256(case, signed_headers, payload_hash) == case["expected"]["canonical_request_sha256"] + for name, value in case["expected"]["headers"].items(): + assert signed_headers[name] == value + + +class _Credentials: + def __init__(self, access_key: str, secret_key: str, token: str | None = None) -> None: + self.access_key = access_key + self.secret_key = secret_key + self.token = token + + +def test_retry_signing_fixture(monkeypatch: pytest.MonkeyPatch) -> None: + case = _cases("retry_signing")[0] + timestamps = iter(_fixed_datetime(value) for value in case["given"]["timestamps"]) + credentials = iter( + _Credentials(access_key, f"{access_key}-secret") for access_key in case["given"]["access_key_ids"] + ) + provider_calls = 0 + + def credentials_provider() -> _Credentials: + nonlocal provider_calls + provider_calls += 1 + return next(credentials) + + _freeze_botocore_time(monkeypatch, timestamps) + + requests: list[httpx.Request] = [] + statuses = iter(case["given"]["response_statuses"]) + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(next(statuses), request=request, json={}) + + body = base64.b64decode(case["given"]["body_base64"]) + with OpenAI( + provider=bedrock( + base_url="https://bedrock-mantle.us-east-1.api.aws/openai/v1", + region="us-east-1", + credential_provider=credentials_provider, + ), + max_retries=case["given"].get("max_retries", 1), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) as client: + client.post( + "/responses", + content=body, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/json"}}, + ) + + assert len(requests) == case["expected"]["attempts"] + assert provider_calls == case["expected"]["credential_provider_calls"] + assert [request.headers["X-Amz-Date"] for request in requests] == case["expected"]["x_amz_dates"] + assert [hashlib.sha256(request.content).hexdigest() for request in requests] == case["expected"]["body_sha256"] + for request, access_key in zip(requests, case["given"]["access_key_ids"]): + assert f"Credential={access_key}/" in request.headers["Authorization"] + + +@pytest.mark.parametrize("case", _cases("body_replay"), ids=lambda case: case["id"]) +def test_body_replay_fixture(case: dict[str, Any]) -> None: + provider_calls = 0 + network_calls = 0 + body_reads = 0 + requests: list[httpx.Request] = [] + + def credentials_provider() -> _Credentials: + nonlocal provider_calls + provider_calls += 1 + return _Credentials("access-key", "secret-key") + + def body() -> Iterator[bytes]: + nonlocal body_reads + body_reads += 1 + for chunk in case["given"].get("chunks_base64", []): + yield base64.b64decode(chunk) + + def handler(request: httpx.Request) -> httpx.Response: + nonlocal network_calls + network_calls += 1 + requests.append(request) + statuses = case["given"].get("response_statuses", [200]) + return httpx.Response(statuses[network_calls - 1], request=request, json={}) + + body_kind = case["given"]["body_kind"] + content: bytes | Iterator[bytes] + if body_kind == "bytes": + content = base64.b64decode(case["given"]["body_base64"]) + else: + assert body_kind == "one_shot_stream" + content = body() + + with OpenAI( + provider=bedrock( + base_url="https://bedrock-mantle.us-east-1.api.aws/openai/v1", + region="us-east-1", + credential_provider=credentials_provider, + ), + max_retries=case["given"].get("max_retries", 1), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) as client: + if case["expected"]["result"] == "bedrock_non_replayable_body": + with pytest.raises(OpenAIError, match="requires a replayable request body"): + client.post("/responses", content=content, cast_to=httpx.Response) + else: + client.post("/responses", content=content, cast_to=httpx.Response) + + assert network_calls == case["expected"].get("network_attempts", case["expected"].get("attempts")) + if body_kind == "bytes": + assert all(request.content == content for request in requests) + assert provider_calls == case["expected"]["attempts"] + else: + assert (body_reads, provider_calls) == (0, 0) + + +@pytest.mark.asyncio +async def test_non_replayable_async_body_fails_before_credentials_or_network() -> None: + provider_calls = 0 + network_calls = 0 + body_reads = 0 + + def credentials_provider() -> _Credentials: + nonlocal provider_calls + provider_calls += 1 + return _Credentials("access-key", "secret-key") + + async def body() -> AsyncIterator[bytes]: + nonlocal body_reads + body_reads += 1 + yield b"body" + + async def handler(request: httpx.Request) -> httpx.Response: + nonlocal network_calls + network_calls += 1 + return httpx.Response(200, request=request) + + async with AsyncOpenAI( + provider=bedrock( + base_url="https://bedrock-mantle.us-east-1.api.aws/openai/v1", + region="us-east-1", + credential_provider=credentials_provider, + ), + http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False), + ) as client: + with pytest.raises(OpenAIError, match="requires a replayable request body"): + await client.post("/responses", content=body(), cast_to=httpx.Response) + + assert (body_reads, provider_calls, network_calls) == (0, 0, 0) + + +@pytest.mark.asyncio +async def test_async_credentials_are_resolved_off_event_loop() -> None: + event_loop_thread = threading.get_ident() + provider_threads: list[int] = [] + + def credentials_provider() -> _Credentials: + provider_threads.append(threading.get_ident()) + return _Credentials("access-key", "secret-key") + + async def handler(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, request=request, json={}) + + async with AsyncOpenAI( + provider=bedrock( + base_url="https://bedrock-mantle.us-east-1.api.aws/openai/v1", + region="us-east-1", + credential_provider=credentials_provider, + ), + http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False), + ) as client: + await client.post("/responses", content=b"{}", cast_to=httpx.Response) + + assert provider_threads + assert all(thread_id != event_loop_thread for thread_id in provider_threads) + + +def test_custom_http_client_auth_cannot_replace_sigv4() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with OpenAI( + provider=bedrock( + base_url="https://bedrock-mantle.us-east-1.api.aws/openai/v1", + region="us-east-1", + access_key_id="access-key", + secret_access_key="secret-key", + ), + http_client=httpx.Client( + auth=httpx.BasicAuth("username", "password"), + transport=httpx.MockTransport(handler), + trust_env=False, + ), + ) as client: + client.get("/models", cast_to=httpx.Response) + + assert requests[0].headers["Authorization"].startswith("AWS4-HMAC-SHA256 Credential=access-key/") + + +def test_sigv4_redirects_are_not_followed() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + if len(requests) == 1: + return httpx.Response(307, request=request, headers={"Location": "/redirected"}) + return httpx.Response(200, request=request) + + with OpenAI( + provider=bedrock( + base_url="https://bedrock-mantle.us-east-1.api.aws/openai/v1", + region="us-east-1", + access_key_id="access-key", + secret_access_key="secret-key", + ), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) as client: + with pytest.raises(APIStatusError) as exc: + client.get("/models", cast_to=httpx.Response) + + assert exc.value.status_code == 307 + assert len(requests) == 1 + + +def test_redaction_fixture(caplog: pytest.LogCaptureFixture) -> None: + case = _cases("redaction")[0] + logger = logging.getLogger("test_bedrock_redaction") + logger.addFilter(SensitiveHeadersFilter()) + + with caplog.at_level(logging.DEBUG): + logger.debug("Request options: %s", {"headers": case["given"]["headers"]}) + + headers = cast(dict[str, Any], caplog.records[0].args)["headers"] + assert headers == case["expected"]["headers"] + for value in case["expected"]["forbidden_substrings"]: + assert value not in caplog.messages[0] + + +def test_fixture_envelope_is_versioned_and_ids_are_unique() -> None: + jsonschema.Draft202012Validator(SCHEMA).validate(FIXTURES) # pyright: ignore[reportUnknownMemberType] + assert FIXTURES["schema_version"] == 1 + assert FIXTURES["suite"] == "aws-bedrock-auth" + ids = [case["id"] for case in FIXTURES["cases"]] + assert len(ids) == len(set(ids)) diff --git a/tests/lib/test_bedrock_credential_chain.py b/tests/lib/test_bedrock_credential_chain.py new file mode 100644 index 0000000000..93e476fe5b --- /dev/null +++ b/tests/lib/test_bedrock_credential_chain.py @@ -0,0 +1,389 @@ +from __future__ import annotations + +import sys +import json +import threading +from typing import Any, Iterator, cast +from pathlib import Path +from datetime import datetime, timezone, timedelta +from contextlib import contextmanager +from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler +from typing_extensions import override + +import httpx +import pytest + +from openai import OpenAI +from openai.providers import bedrock + +_FUTURE_EXPIRATION = "2099-01-01T00:00:00Z" +_AWS_ENVIRONMENT_NAMES = ( + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_SESSION_TOKEN", + "AWS_SECURITY_TOKEN", + "AWS_PROFILE", + "AWS_DEFAULT_PROFILE", + "AWS_REGION", + "AWS_DEFAULT_REGION", + "AWS_CONFIG_FILE", + "AWS_SHARED_CREDENTIALS_FILE", + "AWS_CREDENTIAL_FILE", + "AWS_WEB_IDENTITY_TOKEN_FILE", + "AWS_ROLE_ARN", + "AWS_ROLE_SESSION_NAME", + "AWS_CONTAINER_CREDENTIALS_FULL_URI", + "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", + "AWS_CONTAINER_AUTHORIZATION_TOKEN", + "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", + "AWS_EC2_METADATA_DISABLED", + "AWS_EC2_METADATA_SERVICE_ENDPOINT", + "AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE", + "AWS_BEARER_TOKEN_BEDROCK", + "AWS_BEDROCK_BASE_URL", + "BOTO_CONFIG", + "HTTP_PROXY", + "HTTPS_PROXY", + "ALL_PROXY", + "http_proxy", + "https_proxy", + "all_proxy", + "NO_PROXY", + "no_proxy", +) + + +def _isolate_aws_environment(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> tuple[Path, Path]: + for name in _AWS_ENVIRONMENT_NAMES: + monkeypatch.delenv(name, raising=False) + + config_path = tmp_path / "config" + credentials_path = tmp_path / "credentials" + config_path.write_text("") + credentials_path.write_text("") + monkeypatch.setenv("AWS_CONFIG_FILE", str(config_path)) + monkeypatch.setenv("AWS_SHARED_CREDENTIALS_FILE", str(credentials_path)) + monkeypatch.setenv("AWS_EC2_METADATA_DISABLED", "true") + monkeypatch.setenv("NO_PROXY", "127.0.0.1,localhost") + monkeypatch.setenv("no_proxy", "127.0.0.1,localhost") + return config_path, credentials_path + + +def _signed_request(**provider_options: Any) -> httpx.Request: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with OpenAI( + provider=bedrock(**provider_options), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) as client: + client.get("/models", cast_to=httpx.Response) + + assert len(requests) == 1 + return requests[0] + + +def _credentials_metadata(name: str, *, expiration: str = _FUTURE_EXPIRATION) -> dict[str, str]: + return { + "access_key": f"{name}-access-key", + "secret_key": f"{name}-secret-key", + "token": f"{name}-session-token", + "expiry_time": expiration, + } + + +def _assert_signed_with(request: httpx.Request, name: str, *, region: str) -> None: + assert request.url.host == f"bedrock-mantle.{region}.api.aws" + assert f"Credential={name}-access-key/" in request.headers["Authorization"] + assert request.headers["X-Amz-Security-Token"] == f"{name}-session-token" + + +@contextmanager +def _metadata_server() -> Iterator[tuple[str, list[tuple[str, str, str | None]]]]: + calls: list[tuple[str, str, str | None]] = [] + + class Handler(BaseHTTPRequestHandler): + def _respond(self, body: str, *, content_type: str = "text/plain") -> None: + encoded = body.encode() + self.send_response(200) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(encoded))) + self.end_headers() + self.wfile.write(encoded) + + def do_PUT(self) -> None: + calls.append(("PUT", self.path, self.headers.get("Authorization"))) + if self.path != "/latest/api/token": + self.send_error(404) + return + self._respond("metadata-token") + + def do_GET(self) -> None: + calls.append(("GET", self.path, self.headers.get("Authorization"))) + if self.path == "/container-credentials": + self._respond( + json.dumps( + { + "AccessKeyId": "container-access-key", + "SecretAccessKey": "container-secret-key", + "Token": "container-session-token", + "Expiration": _FUTURE_EXPIRATION, + } + ), + content_type="application/json", + ) + return + if self.path == "/latest/meta-data/iam/security-credentials/": + self._respond("instance-role") + return + if self.path == "/latest/meta-data/iam/security-credentials/instance-role": + self._respond( + json.dumps( + { + "Code": "Success", + "LastUpdated": "2026-01-01T00:00:00Z", + "Type": "AWS-HMAC", + "AccessKeyId": "imds-access-key", + "SecretAccessKey": "imds-secret-key", + "Token": "imds-session-token", + "Expiration": _FUTURE_EXPIRATION, + } + ), + content_type="application/json", + ) + return + self.send_error(404) + + @override + def log_message(self, format: str, *args: Any) -> None: + del format, args + return + + server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + host, port = cast("tuple[str, int]", server.server_address) + try: + yield f"http://{host}:{port}", calls + finally: + server.shutdown() + server.server_close() + thread.join() + + +def test_default_chain_uses_environment_session_credentials(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + _isolate_aws_environment(monkeypatch, tmp_path) + monkeypatch.setenv("AWS_ACCESS_KEY_ID", "environment-access-key") + monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "environment-secret-key") + monkeypatch.setenv("AWS_SESSION_TOKEN", "environment-session-token") + monkeypatch.setenv("AWS_REGION", "us-east-1") + + request = _signed_request() + + _assert_signed_with(request, "environment", region="us-east-1") + + +def test_default_chain_uses_aws_profile_and_shared_files(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + config_path, credentials_path = _isolate_aws_environment(monkeypatch, tmp_path) + credentials_path.write_text( + "[work]\n" + "aws_access_key_id = profile-access-key\n" + "aws_secret_access_key = profile-secret-key\n" + "aws_session_token = profile-session-token\n" + ) + config_path.write_text("[profile work]\nregion = us-west-2\n") + monkeypatch.setenv("AWS_PROFILE", "work") + + request = _signed_request() + + _assert_signed_with(request, "profile", region="us-west-2") + + +def test_default_chain_uses_assume_role_profile(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + config_path, credentials_path = _isolate_aws_environment(monkeypatch, tmp_path) + credentials_path.write_text( + "[source]\naws_access_key_id = source-access-key\naws_secret_access_key = source-secret-key\n" + ) + config_path.write_text( + "[profile assumed]\n" + "role_arn = arn:aws:iam::123456789012:role/example\n" + "source_profile = source\n" + "region = us-east-2\n" + ) + monkeypatch.setenv("AWS_PROFILE", "assumed") + credentials_module = pytest.importorskip("botocore.credentials") + fetches = 0 + + def fetch_credentials(_: object) -> dict[str, str]: + nonlocal fetches + fetches += 1 + return _credentials_metadata("assume-role") + + monkeypatch.setattr(credentials_module.AssumeRoleCredentialFetcher, "fetch_credentials", fetch_credentials) + + request = _signed_request() + + assert fetches == 1 + _assert_signed_with(request, "assume-role", region="us-east-2") + + +def test_default_chain_uses_sso_profile(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + config_path, _ = _isolate_aws_environment(monkeypatch, tmp_path) + config_path.write_text( + "[profile sso]\n" + "sso_session = company\n" + "sso_account_id = 123456789012\n" + "sso_role_name = Example\n" + "region = us-west-1\n\n" + "[sso-session company]\n" + "sso_start_url = https://example.awsapps.com/start\n" + "sso_region = us-east-1\n" + ) + monkeypatch.setenv("AWS_PROFILE", "sso") + credentials_module = pytest.importorskip("botocore.credentials") + fetches = 0 + + def fetch_credentials(_: object) -> dict[str, str]: + nonlocal fetches + fetches += 1 + return _credentials_metadata("sso") + + monkeypatch.setattr(credentials_module.SSOCredentialFetcher, "fetch_credentials", fetch_credentials) + + request = _signed_request() + + assert fetches == 1 + _assert_signed_with(request, "sso", region="us-west-1") + + +def test_default_chain_uses_web_identity(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + _isolate_aws_environment(monkeypatch, tmp_path) + token_path = tmp_path / "web-identity-token" + token_path.write_text("web-identity-token") + monkeypatch.setenv("AWS_ROLE_ARN", "arn:aws:iam::123456789012:role/web-identity") + monkeypatch.setenv("AWS_ROLE_SESSION_NAME", "bedrock-test") + monkeypatch.setenv("AWS_WEB_IDENTITY_TOKEN_FILE", str(token_path)) + monkeypatch.setenv("AWS_REGION", "eu-west-1") + credentials_module = pytest.importorskip("botocore.credentials") + fetches = 0 + + def fetch_credentials(_: object) -> dict[str, str]: + nonlocal fetches + fetches += 1 + return _credentials_metadata("web-identity") + + monkeypatch.setattr( + credentials_module.AssumeRoleWithWebIdentityCredentialFetcher, + "fetch_credentials", + fetch_credentials, + ) + + request = _signed_request() + + assert fetches == 1 + _assert_signed_with(request, "web-identity", region="eu-west-1") + + +def test_default_chain_uses_credential_process(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + config_path, _ = _isolate_aws_environment(monkeypatch, tmp_path) + process_path = tmp_path / "credentials_process.py" + process_output = { + "Version": 1, + "AccessKeyId": "process-access-key", + "SecretAccessKey": "process-secret-key", + "SessionToken": "process-session-token", + "Expiration": _FUTURE_EXPIRATION, + } + process_path.write_text(f"print({json.dumps(process_output)!r})\n") + command = f"{json.dumps(sys.executable)} {json.dumps(str(process_path))}" + config_path.write_text(f"[profile process]\nregion = ap-southeast-2\ncredential_process = {command}\n") + monkeypatch.setenv("AWS_PROFILE", "process") + + request = _signed_request() + + _assert_signed_with(request, "process", region="ap-southeast-2") + + +@pytest.mark.parametrize("use_token_file", [False, True], ids=["ecs", "eks-pod-identity"]) +def test_default_chain_uses_container_credentials( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, use_token_file: bool +) -> None: + _isolate_aws_environment(monkeypatch, tmp_path) + monkeypatch.setenv("AWS_REGION", "us-west-2") + with _metadata_server() as (base_url, metadata_calls): + monkeypatch.setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", f"{base_url}/container-credentials") + expected_authorization = None + if use_token_file: + token_path = tmp_path / "container-authorization-token" + token_path.write_text("pod-identity-token") + monkeypatch.setenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE", str(token_path)) + expected_authorization = "pod-identity-token" + + request = _signed_request() + + assert metadata_calls == [("GET", "/container-credentials", expected_authorization)] + _assert_signed_with(request, "container", region="us-west-2") + + +def test_default_chain_uses_ec2_instance_metadata(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + _isolate_aws_environment(monkeypatch, tmp_path) + monkeypatch.setenv("AWS_REGION", "us-east-1") + monkeypatch.setenv("AWS_EC2_METADATA_DISABLED", "false") + with _metadata_server() as (base_url, metadata_calls): + monkeypatch.setenv("AWS_EC2_METADATA_SERVICE_ENDPOINT", base_url) + + request = _signed_request() + + assert [(method, path) for method, path, _ in metadata_calls] == [ + ("PUT", "/latest/api/token"), + ("GET", "/latest/meta-data/iam/security-credentials/"), + ("GET", "/latest/meta-data/iam/security-credentials/instance-role"), + ] + _assert_signed_with(request, "imds", region="us-east-1") + + +def test_default_chain_refreshes_credentials_before_retry(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + _isolate_aws_environment(monkeypatch, tmp_path) + monkeypatch.setenv("AWS_REGION", "us-east-1") + credentials_module = pytest.importorskip("botocore.credentials") + session_module = pytest.importorskip("botocore.session") + refreshes = 0 + + def expires_soon() -> str: + return (datetime.now(timezone.utc) + timedelta(minutes=5)).isoformat() + + def refresh() -> dict[str, str]: + nonlocal refreshes + refreshes += 1 + return _credentials_metadata(f"refresh-{refreshes}", expiration=expires_soon()) + + credentials = credentials_module.RefreshableCredentials.create_from_metadata( + metadata=_credentials_metadata("initial", expiration=expires_soon()), + refresh_using=refresh, + method="assume-role", + ) + + def get_credentials(_: object) -> Any: + return credentials + + monkeypatch.setattr(session_module.Session, "get_credentials", get_credentials) + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(500 if len(requests) == 1 else 200, request=request, json={}) + + with OpenAI( + provider=bedrock(), + max_retries=1, + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) as client: + client.get("/models", cast_to=httpx.Response) + + assert refreshes == 2 + assert len(requests) == 2 + _assert_signed_with(requests[0], "refresh-1", region="us-east-1") + _assert_signed_with(requests[1], "refresh-2", region="us-east-1") diff --git a/tests/lib/test_bedrock_provider.py b/tests/lib/test_bedrock_provider.py new file mode 100644 index 0000000000..236ce21359 --- /dev/null +++ b/tests/lib/test_bedrock_provider.py @@ -0,0 +1,436 @@ +from __future__ import annotations + +import builtins +from typing import Any, Iterator, cast + +import httpx +import pytest + +import openai.lib._bedrock_auth as bedrock_auth_module +from openai import OpenAI, AsyncOpenAI, OpenAIError +from tests.utils import update_env +from openai._types import Omit +from openai._provider import _create_provider, _ProviderRuntime +from openai.providers import bedrock + + +def test_sync_provider_owns_endpoint_and_bearer_authentication() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + client = OpenAI( + provider=bedrock(region="us-east-1", api_key="bedrock token"), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + client.get("/models", cast_to=httpx.Response) + + assert client.base_url == httpx.URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/") + assert requests[0].url == httpx.URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/models") + assert requests[0].headers["Authorization"] == "Bearer bedrock token" + + +@pytest.mark.asyncio +async def test_async_provider_owns_endpoint_and_bearer_authentication() -> None: + requests: list[httpx.Request] = [] + + async def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + client = AsyncOpenAI( + provider=bedrock(region="us-east-1", token_provider=lambda: "bedrock token"), + http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False), + ) + await client.get("/models", cast_to=httpx.Response) + await client.close() + + assert requests[0].headers["Authorization"] == "Bearer bedrock token" + + +def test_provider_ignores_openai_environment_configuration() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with update_env( + OPENAI_API_KEY="openai token", + OPENAI_BASE_URL="https://api.openai.invalid/v1", + OPENAI_CUSTOM_HEADERS="Authorization: Bearer openai custom token", + ): + client = OpenAI( + provider=bedrock(region="us-east-1", api_key="bedrock token"), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + client.get("/models", cast_to=httpx.Response) + + assert client.api_key == "" + assert requests[0].url.host == "bedrock-mantle.us-east-1.api.aws" + assert requests[0].headers["Authorization"] == "Bearer bedrock token" + + +@pytest.mark.parametrize( + ("option", "value"), + [ + ("api_key", "openai token"), + ("admin_api_key", "admin token"), + ("workload_identity", cast(Any, object())), + ("base_url", "https://api.openai.invalid/v1"), + ], +) +def test_provider_rejects_top_level_authentication_and_routing(option: str, value: object) -> None: + with pytest.raises( + OpenAIError, + match=rf"`provider` cannot be combined with top-level `{option}`.*`bedrock\(\.\.\.\)`", + ): + OpenAI(provider=bedrock(region="us-east-1", api_key="bedrock token"), **{option: value}) # type: ignore[arg-type] + + +def test_provider_survives_with_options_and_can_be_replaced() -> None: + client = OpenAI(provider=bedrock(region="us-east-1", api_key="first")) + + copied = client.with_options(timeout=1) + replaced = client.with_options(provider=bedrock(region="eu-west-1", api_key="second")) + + assert copied.base_url == client.base_url + assert copied._provider is client._provider + assert replaced.base_url == httpx.URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/") + assert replaced._provider is not client._provider + + +def test_switching_to_provider_drops_inherited_openai_metadata() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with update_env( + OPENAI_CUSTOM_HEADERS="X-OpenAI-Ambient: leak", + OPENAI_ORG_ID="ambient-org", + OPENAI_PROJECT_ID="ambient-project", + ): + client = OpenAI( + api_key="openai token", + default_headers={"X-OpenAI-Custom": "leak"}, + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + provider_client = client.with_options(provider=bedrock(region="us-east-1", api_key="bedrock token")) + + provider_client.get("/models", cast_to=httpx.Response) + + headers = requests[0].headers + assert headers["Authorization"] == "Bearer bedrock token" + assert "X-OpenAI-Ambient" not in headers + assert "X-OpenAI-Custom" not in headers + assert "OpenAI-Organization" not in headers + assert "OpenAI-Project" not in headers + + +@pytest.mark.asyncio +async def test_async_switching_to_provider_drops_inherited_openai_metadata() -> None: + requests: list[httpx.Request] = [] + + async def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + client = AsyncOpenAI( + api_key="openai token", + organization="openai-org", + project="openai-project", + default_headers={"X-OpenAI-Custom": "leak"}, + http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False), + ) + provider_client = client.with_options(provider=bedrock(region="us-east-1", api_key="bedrock token")) + + await provider_client.get("/models", cast_to=httpx.Response) + await provider_client.close() + + headers = requests[0].headers + assert headers["Authorization"] == "Bearer bedrock token" + assert "X-OpenAI-Custom" not in headers + assert "OpenAI-Organization" not in headers + assert "OpenAI-Project" not in headers + + +def test_provider_metadata_survives_same_provider_clone_but_not_replacement() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + first_provider = bedrock(region="us-east-1", api_key="first token") + client = OpenAI( + provider=first_provider, + organization="provider-org", + project="provider-project", + default_headers={"X-Provider-Custom": "preserve-me"}, + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + + client.with_options(timeout=1).get("/models", cast_to=httpx.Response) + client.with_options(provider=bedrock(region="us-east-1", api_key="second token")).get( + "/models", cast_to=httpx.Response + ) + + same_provider_headers, replacement_headers = (request.headers for request in requests) + assert same_provider_headers["X-Provider-Custom"] == "preserve-me" + assert same_provider_headers["OpenAI-Organization"] == "provider-org" + assert same_provider_headers["OpenAI-Project"] == "provider-project" + assert "X-Provider-Custom" not in replacement_headers + assert "OpenAI-Organization" not in replacement_headers + assert "OpenAI-Project" not in replacement_headers + + +def test_provider_normalizes_responses_before_status_handling() -> None: + class NormalizingProvider: + name = "normalizing" + + def configure(self) -> _ProviderRuntime: + def normalize(response: httpx.Response) -> httpx.Response: + return httpx.Response(200, request=response.request, json={"normalized": True}) + + return _ProviderRuntime( + name=self.name, + base_url="https://provider.example/v1", + normalize_response=normalize, + ) + + client = OpenAI( + provider=_create_provider(NormalizingProvider()), + max_retries=0, + http_client=httpx.Client( + transport=httpx.MockTransport(lambda request: httpx.Response(500, request=request, json={})), + trust_env=False, + ), + ) + + response = client.get("/models", cast_to=httpx.Response) + + assert response.status_code == 200 + assert response.json() == {"normalized": True} + + +def test_environment_bearer_mode_survives_clone_and_refreshes_each_attempt() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"): + client = OpenAI( + provider=bedrock(region="us-east-1"), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + client.get("/models", cast_to=httpx.Response) + + copied = client.with_options(timeout=1) + with update_env(AWS_BEARER_TOKEN_BEDROCK="second token"): + copied.get("/models", cast_to=httpx.Response) + + assert [request.headers["Authorization"] for request in requests] == ["Bearer first token", "Bearer second token"] + + +def test_provider_can_be_removed_with_explicit_openai_credentials() -> None: + with update_env(OPENAI_CUSTOM_HEADERS=Omit(), OPENAI_ORG_ID=Omit(), OPENAI_PROJECT_ID=Omit()): + client = OpenAI( + provider=bedrock(region="us-east-1", api_key="bedrock token"), + organization="provider-org", + project="provider-project", + default_headers={"X-Provider-Custom": "provider value"}, + ) + + copied = client.with_options(provider=None, api_key="openai token") + + assert copied._provider is None + assert copied.api_key == "openai token" + assert copied.base_url == httpx.URL("https://api.openai.com/v1/") + assert copied.organization is None + assert copied.project is None + assert "X-Provider-Custom" not in copied.default_headers + + +def test_bearer_provider_does_not_load_botocore(monkeypatch: pytest.MonkeyPatch) -> None: + def load_botocore() -> None: + raise AssertionError("bearer authentication must not import botocore") + + monkeypatch.setattr(bedrock_auth_module, "_load_botocore", load_botocore) + + client = OpenAI(provider=bedrock(region="us-east-1", api_key="bedrock token")) + request = client._build_request(client._prepare_options(_get_options())) + client._prepare_request(request) + + assert request.headers["Authorization"] == "Bearer bedrock token" + + +def test_missing_aws_dependency_is_actionable_and_lazy(monkeypatch: pytest.MonkeyPatch) -> None: + real_import = builtins.__import__ + network_calls = 0 + + def import_module( + name: str, + globals: dict[str, Any] | None = None, + locals: dict[str, Any] | None = None, + fromlist: tuple[str, ...] = (), + level: int = 0, + ) -> Any: + if name.startswith("botocore"): + raise ImportError(name) + return real_import(name, globals, locals, fromlist, level) + + def handler(request: httpx.Request) -> httpx.Response: + nonlocal network_calls + network_calls += 1 + return httpx.Response(200, request=request) + + monkeypatch.setattr(builtins, "__import__", import_module) + client = OpenAI( + provider=bedrock(region="us-east-1"), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + + with pytest.raises(OpenAIError, match=r"pip install openai\[bedrock\]"): + client.get("/models", cast_to=httpx.Response) + + assert network_calls == 0 + + +def test_api_key_none_skips_environment_bearer_fallback() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with update_env( + AWS_BEARER_TOKEN_BEDROCK="environment bearer", + AWS_ACCESS_KEY_ID="access key", + AWS_SECRET_ACCESS_KEY="secret key", + ): + client = OpenAI( + provider=bedrock(region="us-east-1", api_key=None), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + client.get("/models", cast_to=httpx.Response) + + assert requests[0].headers["Authorization"].startswith("AWS4-HMAC-SHA256 Credential=access key/") + + +def test_provider_rejects_custom_authorization_before_network() -> None: + network_calls = 0 + + def handler(request: httpx.Request) -> httpx.Response: + nonlocal network_calls + network_calls += 1 + return httpx.Response(200, request=request) + + client = OpenAI( + provider=bedrock(region="us-east-1", api_key="bedrock token"), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + + with pytest.raises(OpenAIError, match="cannot be combined with a custom `Authorization` header"): + client.get( + "/models", + cast_to=httpx.Response, + options={"headers": {"Authorization": "Bearer custom"}}, + ) + + assert network_calls == 0 + + +def test_bearer_provider_rejects_cross_origin_requests_before_resolving_credentials() -> None: + network_calls = 0 + provider_calls = 0 + + def token_provider() -> str: + nonlocal provider_calls + provider_calls += 1 + return "bedrock token" + + def handler(request: httpx.Request) -> httpx.Response: + nonlocal network_calls + network_calls += 1 + return httpx.Response(200, request=request) + + client = OpenAI( + provider=bedrock(base_url="https://bedrock.example/openai/v1", token_provider=token_provider), + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + + with pytest.raises(OpenAIError, match="origin other than the configured provider URL"): + client.get("https://attacker.example/steal", cast_to=httpx.Response) + + assert (provider_calls, network_calls) == (0, 0) + + +@pytest.mark.asyncio +async def test_async_bearer_provider_rejects_cross_origin_requests_before_resolving_credentials() -> None: + network_calls = 0 + provider_calls = 0 + + async def token_provider() -> str: + nonlocal provider_calls + provider_calls += 1 + return "bedrock token" + + async def handler(request: httpx.Request) -> httpx.Response: + nonlocal network_calls + network_calls += 1 + return httpx.Response(200, request=request) + + client = AsyncOpenAI( + provider=bedrock(base_url="https://bedrock.example/openai/v1", token_provider=token_provider), + http_client=httpx.AsyncClient(transport=httpx.MockTransport(handler), trust_env=False), + ) + + with pytest.raises(OpenAIError, match="origin other than the configured provider URL"): + await client.get("https://attacker.example/steal", cast_to=httpx.Response) + + await client.close() + assert (provider_calls, network_calls) == (0, 0) + + +def test_bearer_provider_allows_one_shot_body_when_retries_are_disabled() -> None: + requests: list[httpx.Request] = [] + + def body() -> Iterator[bytes]: + yield b"body" + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request) + + client = OpenAI( + provider=bedrock(base_url="https://bedrock.example/openai/v1", api_key="bedrock token"), + max_retries=0, + http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False), + ) + + client.post("/responses", content=body(), cast_to=httpx.Response) + + assert requests[0].content == b"body" + + +def test_opaque_provider_repr_does_not_expose_credentials() -> None: + provider = bedrock( + region="us-east-1", + access_key_id="secret access key id", + secret_access_key="secret access key", + session_token="secret session token", + ) + + assert "secret" not in repr(provider) + + +def _get_options() -> Any: + from openai._models import FinalRequestOptions + + return FinalRequestOptions(method="get", url="/models", security={"bearer_auth": True}) diff --git a/tests/test_module_client.py b/tests/test_module_client.py index 23bcb61716..cb509d3d19 100644 --- a/tests/test_module_client.py +++ b/tests/test_module_client.py @@ -253,6 +253,30 @@ def test_bedrock_module_api_key_overrides_cached_env_token_after_load() -> None: assert client.api_key == "new Bedrock token" +def test_bedrock_module_api_key_switches_cached_aws_client_to_bearer() -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with fresh_env(): + openai.api_type = "amazon-bedrock" + openai.http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False) + _os.environ["AWS_ACCESS_KEY_ID"] = "access key" + _os.environ["AWS_SECRET_ACCESS_KEY"] = "secret key" + _os.environ["AWS_REGION"] = "us-west-2" + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + assert client._uses_aws_auth() + + openai.api_key = "new Bedrock token" + client.get("/models", cast_to=httpx.Response) + + assert requests[0].headers["Authorization"] == "Bearer new Bedrock token" + + def test_bedrock_api_type_uses_token_provider_without_mutating_module_api_key() -> None: with fresh_env(): openai.api_type = "amazon-bedrock" @@ -263,3 +287,32 @@ def test_bedrock_api_type_uses_token_provider_without_mutating_module_api_key() assert isinstance(client, BedrockOpenAI) assert client._refresh_api_key() == "provider Bedrock token" assert openai.api_key is None + + +def test_bedrock_module_api_key_overrides_cached_token_provider() -> None: + requests: list[httpx.Request] = [] + provider_calls = 0 + + def token_provider() -> str: + nonlocal provider_calls + provider_calls += 1 + raise AssertionError("the replaced token provider must not be called") + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, request=request, json={}) + + with fresh_env(): + openai.api_type = "amazon-bedrock" + openai.bedrock_token_provider = token_provider + openai.http_client = httpx.Client(transport=httpx.MockTransport(handler), trust_env=False) + _os.environ["AWS_REGION"] = "us-west-2" + + client = openai.responses._client + assert isinstance(client, BedrockOpenAI) + + openai.api_key = "new Bedrock token" + client.get("/models", cast_to=httpx.Response) + + assert provider_calls == 0 + assert requests[0].headers["Authorization"] == "Bearer new Bedrock token" From 6d9262d5c666a1e4d47f63178db907ba3087ac5d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 20:35:33 +0000 Subject: [PATCH 398/408] release: 2.44.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bee3a52fce..e59f0b3e3b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.43.0" + ".": "2.44.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a467b3b53..93b063cfc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.44.0 (2026-06-24) + +Full Changelog: [v2.43.0...v2.44.0](https://github.com/openai/openai-python/compare/v2.43.0...v2.44.0) + +### Bug Fixes + +* **auth:** prioritize first auth header ([797e336](https://github.com/openai/openai-python/commit/797e3362e222ae14e587a4543b76a54d8992d66c)) + ## 2.43.0 (2026-06-17) Full Changelog: [v2.42.0...v2.43.0](https://github.com/openai/openai-python/compare/v2.42.0...v2.43.0) diff --git a/pyproject.toml b/pyproject.toml index 407dd73b12..c63c46452e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.43.0" +version = "2.44.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 6c046bf3b6..e27a90e2a8 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.43.0" # x-release-please-version +__version__ = "2.44.0" # x-release-please-version From f16fbbd2bd25dc1ff150b5f78dbd15ff6bab6d91 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:01:33 -0400 Subject: [PATCH 399/408] release: 2.45.0 (#3478) * feat(api): gpt-5.6-sol updates * fix(api): restore beta resource accessors * chore: retrigger release automation * release: 2.45.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: Alex Chang --- .release-please-manifest.json | 2 +- .stats.yml | 8 +- CHANGELOG.md | 18 + api.md | 191 + examples/responses/multi_agent_streaming.py | 40 + examples/responses/multi_agent_websocket.py | 46 + pyproject.toml | 2 +- src/openai/_version.py | 2 +- src/openai/lib/_parsing/_responses.py | 2 + src/openai/resources/beta/__init__.py | 14 + src/openai/resources/beta/assistants.py | 76 +- src/openai/resources/beta/beta.py | 32 + .../resources/beta/responses/__init__.py | 47 + .../resources/beta/responses/input_items.py | 238 + .../resources/beta/responses/input_tokens.py | 340 ++ .../resources/beta/responses/responses.py | 5262 +++++++++++++++++ .../resources/beta/threads/runs/runs.py | 114 +- .../resources/chat/completions/completions.py | 246 +- src/openai/resources/realtime/calls.py | 4 + src/openai/resources/responses/responses.py | 177 +- src/openai/resources/webhooks/api.md | 1 + src/openai/types/beta/__init__.py | 366 ++ .../types/beta/assistant_create_params.py | 22 +- .../types/beta/assistant_update_params.py | 22 +- .../types/beta/beta_apply_patch_tool.py | 18 + .../types/beta/beta_apply_patch_tool_param.py | 18 + .../types/beta/beta_compacted_response.py | 33 + src/openai/types/beta/beta_computer_action.py | 196 + .../types/beta/beta_computer_action_list.py | 10 + .../beta/beta_computer_action_list_param.py | 198 + .../types/beta/beta_computer_action_param.py | 195 + src/openai/types/beta/beta_computer_tool.py | 17 + .../types/beta/beta_computer_tool_param.py | 17 + .../beta/beta_computer_use_preview_tool.py | 26 + .../beta_computer_use_preview_tool_param.py | 26 + src/openai/types/beta/beta_container_auto.py | 36 + .../types/beta/beta_container_auto_param.py | 35 + ...beta_container_network_policy_allowlist.py | 20 + ...ontainer_network_policy_allowlist_param.py | 22 + .../beta_container_network_policy_disabled.py | 12 + ...container_network_policy_disabled_param.py | 12 + ..._container_network_policy_domain_secret.py | 16 + ...iner_network_policy_domain_secret_param.py | 18 + .../types/beta/beta_container_reference.py | 15 + .../beta/beta_container_reference_param.py | 15 + src/openai/types/beta/beta_custom_tool.py | 57 + .../types/beta/beta_custom_tool_param.py | 56 + .../types/beta/beta_easy_input_message.py | 42 + .../beta/beta_easy_input_message_param.py | 43 + .../types/beta/beta_file_search_tool.py | 153 + .../types/beta/beta_file_search_tool_param.py | 155 + .../types/beta/beta_function_shell_tool.py | 28 + .../beta/beta_function_shell_tool_param.py | 26 + src/openai/types/beta/beta_function_tool.py | 45 + .../types/beta/beta_function_tool_param.py | 45 + src/openai/types/beta/beta_inline_skill.py | 22 + .../types/beta/beta_inline_skill_param.py | 23 + .../types/beta/beta_inline_skill_source.py | 20 + .../beta/beta_inline_skill_source_param.py | 20 + .../types/beta/beta_local_environment.py | 17 + .../beta/beta_local_environment_param.py | 18 + src/openai/types/beta/beta_local_skill.py | 16 + .../types/beta/beta_local_skill_param.py | 18 + src/openai/types/beta/beta_namespace_tool.py | 58 + .../types/beta/beta_namespace_tool_param.py | 58 + src/openai/types/beta/beta_response.py | 616 ++ .../beta_response_apply_patch_tool_call.py | 115 + ...a_response_apply_patch_tool_call_output.py | 61 + .../beta/beta_response_audio_delta_event.py | 31 + .../beta/beta_response_audio_done_event.py | 28 + ...a_response_audio_transcript_delta_event.py | 31 + ...ta_response_audio_transcript_done_event.py | 28 + ..._code_interpreter_call_code_delta_event.py | 40 + ...e_code_interpreter_call_code_done_event.py | 37 + ...e_code_interpreter_call_completed_event.py | 37 + ...code_interpreter_call_in_progress_event.py | 37 + ...ode_interpreter_call_interpreting_event.py | 37 + ...eta_response_code_interpreter_tool_call.py | 71 + ...sponse_code_interpreter_tool_call_param.py | 70 + .../beta/beta_response_compaction_item.py | 36 + .../beta_response_compaction_item_param.py | 33 + ...ta_response_compaction_item_param_param.py | 33 + .../beta/beta_response_completed_event.py | 32 + .../beta/beta_response_computer_tool_call.py | 69 + ...response_computer_tool_call_output_item.py | 62 + ...se_computer_tool_call_output_screenshot.py | 24 + ...puter_tool_call_output_screenshot_param.py | 23 + .../beta_response_computer_tool_call_param.py | 70 + .../beta/beta_response_container_reference.py | 16 + .../beta_response_content_part_added_event.py | 58 + .../beta_response_content_part_done_event.py | 58 + .../beta/beta_response_conversation_param.py | 12 + .../beta_response_conversation_param_param.py | 14 + .../types/beta/beta_response_created_event.py | 32 + .../beta/beta_response_custom_tool_call.py | 58 + ...onse_custom_tool_call_input_delta_event.py | 37 + ...ponse_custom_tool_call_input_done_event.py | 37 + .../beta_response_custom_tool_call_item.py | 25 + .../beta_response_custom_tool_call_output.py | 71 + ...a_response_custom_tool_call_output_item.py | 25 + ..._response_custom_tool_call_output_param.py | 71 + .../beta_response_custom_tool_call_param.py | 57 + src/openai/types/beta/beta_response_error.py | 37 + .../types/beta/beta_response_error_event.py | 37 + .../types/beta/beta_response_failed_event.py | 32 + ...sponse_file_search_call_completed_event.py | 34 + ...onse_file_search_call_in_progress_event.py | 34 + ...sponse_file_search_call_searching_event.py | 34 + .../beta_response_file_search_tool_call.py | 67 + ...ta_response_file_search_tool_call_param.py | 69 + .../beta/beta_response_format_text_config.py | 35 + .../beta_response_format_text_config_param.py | 33 + ...response_format_text_json_schema_config.py | 49 + ...se_format_text_json_schema_config_param.py | 47 + ...nse_function_call_arguments_delta_event.py | 39 + ...onse_function_call_arguments_done_event.py | 39 + ...beta_response_function_call_output_item.py | 16 + ...response_function_call_output_item_list.py | 10 + ...se_function_call_output_item_list_param.py | 18 + ...esponse_function_call_output_item_param.py | 16 + ...onse_function_shell_call_output_content.py | 42 + ...unction_shell_call_output_content_param.py | 41 + .../beta_response_function_shell_tool_call.py | 94 + ...esponse_function_shell_tool_call_output.py | 119 + .../beta/beta_response_function_tool_call.py | 69 + .../beta_response_function_tool_call_item.py | 29 + ...response_function_tool_call_output_item.py | 79 + .../beta_response_function_tool_call_param.py | 68 + .../beta/beta_response_function_web_search.py | 102 + ...beta_response_function_web_search_param.py | 103 + ...response_image_gen_call_completed_event.py | 36 + ...esponse_image_gen_call_generating_event.py | 36 + ...sponse_image_gen_call_in_progress_event.py | 34 + ...onse_image_gen_call_partial_image_event.py | 43 + .../beta/beta_response_in_progress_event.py | 32 + .../types/beta/beta_response_includable.py | 16 + .../beta/beta_response_incomplete_event.py | 32 + .../beta_response_inject_created_event.py | 30 + .../types/beta/beta_response_inject_event.py | 26 + .../beta/beta_response_inject_event_param.py | 27 + .../beta/beta_response_inject_failed_event.py | 49 + src/openai/types/beta/beta_response_input.py | 10 + .../types/beta/beta_response_input_content.py | 15 + .../beta/beta_response_input_content_param.py | 16 + .../types/beta/beta_response_input_file.py | 53 + .../beta/beta_response_input_file_content.py | 53 + .../beta_response_input_file_content_param.py | 53 + .../beta/beta_response_input_file_param.py | 53 + .../types/beta/beta_response_input_image.py | 50 + .../beta/beta_response_input_image_content.py | 50 + ...beta_response_input_image_content_param.py | 50 + .../beta/beta_response_input_image_param.py | 50 + .../types/beta/beta_response_input_item.py | 1160 ++++ .../beta/beta_response_input_item_param.py | 1147 ++++ ...eta_response_input_message_content_list.py | 10 + ...sponse_input_message_content_list_param.py | 18 + .../beta/beta_response_input_message_item.py | 43 + .../types/beta/beta_response_input_param.py | 1150 ++++ .../types/beta/beta_response_input_text.py | 35 + .../beta/beta_response_input_text_content.py | 35 + .../beta_response_input_text_content_param.py | 35 + .../beta/beta_response_input_text_param.py | 34 + src/openai/types/beta/beta_response_item.py | 619 ++ .../beta/beta_response_local_environment.py | 14 + ...response_mcp_call_arguments_delta_event.py | 42 + ..._response_mcp_call_arguments_done_event.py | 37 + .../beta_response_mcp_call_completed_event.py | 34 + .../beta_response_mcp_call_failed_event.py | 34 + ...eta_response_mcp_call_in_progress_event.py | 34 + ...response_mcp_list_tools_completed_event.py | 34 + ...ta_response_mcp_list_tools_failed_event.py | 34 + ...sponse_mcp_list_tools_in_progress_event.py | 36 + .../types/beta/beta_response_output_item.py | 617 ++ .../beta_response_output_item_added_event.py | 35 + .../beta_response_output_item_done_event.py | 35 + .../beta/beta_response_output_message.py | 56 + .../beta_response_output_message_param.py | 54 + .../beta/beta_response_output_refusal.py | 17 + .../beta_response_output_refusal_param.py | 17 + .../types/beta/beta_response_output_text.py | 131 + ...onse_output_text_annotation_added_event.py | 43 + .../beta/beta_response_output_text_param.py | 129 + src/openai/types/beta/beta_response_prompt.py | 33 + .../types/beta/beta_response_prompt_param.py | 34 + .../types/beta/beta_response_queued_event.py | 32 + .../beta/beta_response_reasoning_item.py | 72 + .../beta_response_reasoning_item_param.py | 72 + ...onse_reasoning_summary_part_added_event.py | 50 + ...ponse_reasoning_summary_part_done_event.py | 57 + ...onse_reasoning_summary_text_delta_event.py | 40 + ...ponse_reasoning_summary_text_done_event.py | 40 + ...eta_response_reasoning_text_delta_event.py | 40 + ...beta_response_reasoning_text_done_event.py | 40 + .../beta/beta_response_refusal_delta_event.py | 40 + .../beta/beta_response_refusal_done_event.py | 40 + src/openai/types/beta/beta_response_status.py | 7 + .../types/beta/beta_response_stream_event.py | 120 + .../types/beta/beta_response_text_config.py | 43 + .../beta/beta_response_text_config_param.py | 44 + .../beta/beta_response_text_delta_event.py | 68 + .../beta/beta_response_text_done_event.py | 68 + .../beta/beta_response_tool_search_call.py | 41 + .../beta_response_tool_search_output_item.py | 42 + ..._response_tool_search_output_item_param.py | 39 + ...nse_tool_search_output_item_param_param.py | 40 + src/openai/types/beta/beta_response_usage.py | 47 + ...esponse_web_search_call_completed_event.py | 34 + ...ponse_web_search_call_in_progress_event.py | 34 + ...esponse_web_search_call_searching_event.py | 34 + .../types/beta/beta_responses_client_event.py | 619 ++ .../beta/beta_responses_client_event_param.py | 616 ++ .../types/beta/beta_responses_server_event.py | 124 + src/openai/types/beta/beta_skill_reference.py | 19 + .../types/beta/beta_skill_reference_param.py | 18 + src/openai/types/beta/beta_tool.py | 374 ++ .../types/beta/beta_tool_choice_allowed.py | 38 + .../beta/beta_tool_choice_allowed_param.py | 38 + .../beta/beta_tool_choice_apply_patch.py | 14 + .../beta_tool_choice_apply_patch_param.py | 14 + .../types/beta/beta_tool_choice_custom.py | 17 + .../beta/beta_tool_choice_custom_param.py | 17 + .../types/beta/beta_tool_choice_function.py | 17 + .../beta/beta_tool_choice_function_param.py | 17 + src/openai/types/beta/beta_tool_choice_mcp.py | 23 + .../types/beta/beta_tool_choice_mcp_param.py | 23 + .../types/beta/beta_tool_choice_options.py | 7 + .../types/beta/beta_tool_choice_shell.py | 14 + .../beta/beta_tool_choice_shell_param.py | 14 + .../types/beta/beta_tool_choice_types.py | 40 + .../beta/beta_tool_choice_types_param.py | 42 + src/openai/types/beta/beta_tool_param.py | 369 ++ .../types/beta/beta_tool_search_tool.py | 24 + .../types/beta/beta_tool_search_tool_param.py | 24 + .../beta/beta_web_search_preview_tool.py | 58 + .../beta_web_search_preview_tool_param.py | 58 + src/openai/types/beta/beta_web_search_tool.py | 73 + .../types/beta/beta_web_search_tool_param.py | 75 + .../types/beta/response_compact_params.py | 192 + .../types/beta/response_create_params.py | 621 ++ .../types/beta/response_retrieve_params.py | 62 + src/openai/types/beta/responses/__init__.py | 8 + .../beta/responses/beta_response_item_list.py | 28 + .../beta/responses/input_item_list_params.py | 37 + .../responses/input_token_count_params.py | 216 + .../responses/input_token_count_response.py | 13 + .../types/beta/threads/run_create_params.py | 22 +- .../chat_completion_content_part_image.py | 19 +- ...hat_completion_content_part_image_param.py | 19 +- ...mpletion_content_part_input_audio_param.py | 19 +- .../chat_completion_content_part_param.py | 19 +- .../chat/chat_completion_content_part_text.py | 20 +- ...chat_completion_content_part_text_param.py | 19 +- .../types/chat/completion_create_params.py | 99 +- src/openai/types/completion_usage.py | 3 + .../computer_screenshot_content.py | 19 +- .../types/conversations/conversation_item.py | 38 + ...create_eval_completions_run_data_source.py | 22 +- ..._eval_completions_run_data_source_param.py | 22 +- src/openai/types/evals/run_cancel_response.py | 44 +- src/openai/types/evals/run_create_params.py | 44 +- src/openai/types/evals/run_create_response.py | 44 +- src/openai/types/evals/run_list_response.py | 44 +- .../types/evals/run_retrieve_response.py | 44 +- .../types/graders/score_model_grader.py | 22 +- .../types/graders/score_model_grader_param.py | 22 +- .../types/realtime/call_accept_params.py | 2 + .../realtime_response_create_mcp_tool.py | 3 + ...realtime_response_create_mcp_tool_param.py | 5 +- .../realtime_session_create_request.py | 2 + .../realtime_session_create_request_param.py | 2 + .../realtime_session_create_response.py | 5 + .../realtime/realtime_tools_config_param.py | 3 + .../realtime/realtime_tools_config_union.py | 3 + .../realtime_tools_config_union_param.py | 5 +- .../types/responses/apply_patch_tool.py | 4 + .../types/responses/apply_patch_tool_param.py | 4 + src/openai/types/responses/custom_tool.py | 5 +- .../types/responses/custom_tool_param.py | 4 + .../types/responses/function_shell_tool.py | 5 +- .../responses/function_shell_tool_param.py | 5 +- src/openai/types/responses/function_tool.py | 13 +- .../types/responses/function_tool_param.py | 13 +- .../responses/input_token_count_params.py | 16 +- src/openai/types/responses/namespace_tool.py | 16 +- .../types/responses/namespace_tool_param.py | 16 +- src/openai/types/responses/parsed_response.py | 4 + src/openai/types/responses/response.py | 39 +- .../response_apply_patch_tool_call.py | 20 + .../response_apply_patch_tool_call_output.py | 24 +- .../responses/response_compact_params.py | 43 +- .../types/responses/response_create_params.py | 86 +- .../responses/response_custom_tool_call.py | 24 +- .../response_custom_tool_call_output.py | 21 +- .../response_custom_tool_call_output_param.py | 23 +- .../response_custom_tool_call_param.py | 22 +- src/openai/types/responses/response_error.py | 1 + .../response_function_shell_tool_call.py | 19 +- ...esponse_function_shell_tool_call_output.py | 20 + .../responses/response_function_tool_call.py | 24 +- ...response_function_tool_call_output_item.py | 21 +- .../response_function_tool_call_param.py | 22 +- .../types/responses/response_input_file.py | 27 +- .../responses/response_input_file_content.py | 27 +- .../response_input_file_content_param.py | 27 +- .../responses/response_input_file_param.py | 27 +- .../types/responses/response_input_image.py | 19 +- .../responses/response_input_image_content.py | 19 +- .../response_input_image_content_param.py | 19 +- .../responses/response_input_image_param.py | 19 +- .../types/responses/response_input_item.py | 157 + .../responses/response_input_item_param.py | 147 + .../types/responses/response_input_param.py | 147 + .../types/responses/response_input_text.py | 20 +- .../responses/response_input_text_content.py | 20 +- .../response_input_text_content_param.py | 20 +- .../responses/response_input_text_param.py | 19 +- src/openai/types/responses/response_item.py | 38 + .../types/responses/response_output_item.py | 38 + ...ponse_reasoning_summary_part_done_event.py | 8 + src/openai/types/responses/response_usage.py | 3 + .../types/responses/responses_client_event.py | 95 +- .../responses/responses_client_event_param.py | 86 +- src/openai/types/responses/tool.py | 13 + src/openai/types/responses/tool_param.py | 15 +- src/openai/types/shared/chat_model.py | 3 + src/openai/types/shared/reasoning.py | 30 +- src/openai/types/shared/reasoning_effort.py | 2 +- src/openai/types/shared_params/chat_model.py | 3 + src/openai/types/shared_params/reasoning.py | 30 +- .../types/shared_params/reasoning_effort.py | 2 +- src/openai/types/webhooks/__init__.py | 3 + ...safety_identifier_blocked_webhook_event.py | 46 + .../types/webhooks/unwrap_webhook_event.py | 2 + .../api_resources/beta/responses/__init__.py | 1 + .../beta/responses/test_input_items.py | 125 + .../beta/responses/test_input_tokens.py | 152 + tests/api_resources/beta/test_responses.py | 945 +++ tests/api_resources/beta/test_threads.py | 8 +- tests/api_resources/beta/threads/test_runs.py | 8 +- tests/api_resources/chat/test_completions.py | 48 +- .../responses/test_input_tokens.py | 6 + tests/api_resources/test_responses.py | 84 +- tests/lib/responses/test_responses.py | 33 +- 343 files changed, 27534 insertions(+), 633 deletions(-) create mode 100644 examples/responses/multi_agent_streaming.py create mode 100644 examples/responses/multi_agent_websocket.py create mode 100644 src/openai/resources/beta/responses/__init__.py create mode 100644 src/openai/resources/beta/responses/input_items.py create mode 100644 src/openai/resources/beta/responses/input_tokens.py create mode 100644 src/openai/resources/beta/responses/responses.py create mode 100644 src/openai/types/beta/beta_apply_patch_tool.py create mode 100644 src/openai/types/beta/beta_apply_patch_tool_param.py create mode 100644 src/openai/types/beta/beta_compacted_response.py create mode 100644 src/openai/types/beta/beta_computer_action.py create mode 100644 src/openai/types/beta/beta_computer_action_list.py create mode 100644 src/openai/types/beta/beta_computer_action_list_param.py create mode 100644 src/openai/types/beta/beta_computer_action_param.py create mode 100644 src/openai/types/beta/beta_computer_tool.py create mode 100644 src/openai/types/beta/beta_computer_tool_param.py create mode 100644 src/openai/types/beta/beta_computer_use_preview_tool.py create mode 100644 src/openai/types/beta/beta_computer_use_preview_tool_param.py create mode 100644 src/openai/types/beta/beta_container_auto.py create mode 100644 src/openai/types/beta/beta_container_auto_param.py create mode 100644 src/openai/types/beta/beta_container_network_policy_allowlist.py create mode 100644 src/openai/types/beta/beta_container_network_policy_allowlist_param.py create mode 100644 src/openai/types/beta/beta_container_network_policy_disabled.py create mode 100644 src/openai/types/beta/beta_container_network_policy_disabled_param.py create mode 100644 src/openai/types/beta/beta_container_network_policy_domain_secret.py create mode 100644 src/openai/types/beta/beta_container_network_policy_domain_secret_param.py create mode 100644 src/openai/types/beta/beta_container_reference.py create mode 100644 src/openai/types/beta/beta_container_reference_param.py create mode 100644 src/openai/types/beta/beta_custom_tool.py create mode 100644 src/openai/types/beta/beta_custom_tool_param.py create mode 100644 src/openai/types/beta/beta_easy_input_message.py create mode 100644 src/openai/types/beta/beta_easy_input_message_param.py create mode 100644 src/openai/types/beta/beta_file_search_tool.py create mode 100644 src/openai/types/beta/beta_file_search_tool_param.py create mode 100644 src/openai/types/beta/beta_function_shell_tool.py create mode 100644 src/openai/types/beta/beta_function_shell_tool_param.py create mode 100644 src/openai/types/beta/beta_function_tool.py create mode 100644 src/openai/types/beta/beta_function_tool_param.py create mode 100644 src/openai/types/beta/beta_inline_skill.py create mode 100644 src/openai/types/beta/beta_inline_skill_param.py create mode 100644 src/openai/types/beta/beta_inline_skill_source.py create mode 100644 src/openai/types/beta/beta_inline_skill_source_param.py create mode 100644 src/openai/types/beta/beta_local_environment.py create mode 100644 src/openai/types/beta/beta_local_environment_param.py create mode 100644 src/openai/types/beta/beta_local_skill.py create mode 100644 src/openai/types/beta/beta_local_skill_param.py create mode 100644 src/openai/types/beta/beta_namespace_tool.py create mode 100644 src/openai/types/beta/beta_namespace_tool_param.py create mode 100644 src/openai/types/beta/beta_response.py create mode 100644 src/openai/types/beta/beta_response_apply_patch_tool_call.py create mode 100644 src/openai/types/beta/beta_response_apply_patch_tool_call_output.py create mode 100644 src/openai/types/beta/beta_response_audio_delta_event.py create mode 100644 src/openai/types/beta/beta_response_audio_done_event.py create mode 100644 src/openai/types/beta/beta_response_audio_transcript_delta_event.py create mode 100644 src/openai/types/beta/beta_response_audio_transcript_done_event.py create mode 100644 src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py create mode 100644 src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py create mode 100644 src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py create mode 100644 src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py create mode 100644 src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py create mode 100644 src/openai/types/beta/beta_response_code_interpreter_tool_call.py create mode 100644 src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py create mode 100644 src/openai/types/beta/beta_response_compaction_item.py create mode 100644 src/openai/types/beta/beta_response_compaction_item_param.py create mode 100644 src/openai/types/beta/beta_response_compaction_item_param_param.py create mode 100644 src/openai/types/beta/beta_response_completed_event.py create mode 100644 src/openai/types/beta/beta_response_computer_tool_call.py create mode 100644 src/openai/types/beta/beta_response_computer_tool_call_output_item.py create mode 100644 src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py create mode 100644 src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py create mode 100644 src/openai/types/beta/beta_response_computer_tool_call_param.py create mode 100644 src/openai/types/beta/beta_response_container_reference.py create mode 100644 src/openai/types/beta/beta_response_content_part_added_event.py create mode 100644 src/openai/types/beta/beta_response_content_part_done_event.py create mode 100644 src/openai/types/beta/beta_response_conversation_param.py create mode 100644 src/openai/types/beta/beta_response_conversation_param_param.py create mode 100644 src/openai/types/beta/beta_response_created_event.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call_item.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call_output.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call_output_item.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call_output_param.py create mode 100644 src/openai/types/beta/beta_response_custom_tool_call_param.py create mode 100644 src/openai/types/beta/beta_response_error.py create mode 100644 src/openai/types/beta/beta_response_error_event.py create mode 100644 src/openai/types/beta/beta_response_failed_event.py create mode 100644 src/openai/types/beta/beta_response_file_search_call_completed_event.py create mode 100644 src/openai/types/beta/beta_response_file_search_call_in_progress_event.py create mode 100644 src/openai/types/beta/beta_response_file_search_call_searching_event.py create mode 100644 src/openai/types/beta/beta_response_file_search_tool_call.py create mode 100644 src/openai/types/beta/beta_response_file_search_tool_call_param.py create mode 100644 src/openai/types/beta/beta_response_format_text_config.py create mode 100644 src/openai/types/beta/beta_response_format_text_config_param.py create mode 100644 src/openai/types/beta/beta_response_format_text_json_schema_config.py create mode 100644 src/openai/types/beta/beta_response_format_text_json_schema_config_param.py create mode 100644 src/openai/types/beta/beta_response_function_call_arguments_delta_event.py create mode 100644 src/openai/types/beta/beta_response_function_call_arguments_done_event.py create mode 100644 src/openai/types/beta/beta_response_function_call_output_item.py create mode 100644 src/openai/types/beta/beta_response_function_call_output_item_list.py create mode 100644 src/openai/types/beta/beta_response_function_call_output_item_list_param.py create mode 100644 src/openai/types/beta/beta_response_function_call_output_item_param.py create mode 100644 src/openai/types/beta/beta_response_function_shell_call_output_content.py create mode 100644 src/openai/types/beta/beta_response_function_shell_call_output_content_param.py create mode 100644 src/openai/types/beta/beta_response_function_shell_tool_call.py create mode 100644 src/openai/types/beta/beta_response_function_shell_tool_call_output.py create mode 100644 src/openai/types/beta/beta_response_function_tool_call.py create mode 100644 src/openai/types/beta/beta_response_function_tool_call_item.py create mode 100644 src/openai/types/beta/beta_response_function_tool_call_output_item.py create mode 100644 src/openai/types/beta/beta_response_function_tool_call_param.py create mode 100644 src/openai/types/beta/beta_response_function_web_search.py create mode 100644 src/openai/types/beta/beta_response_function_web_search_param.py create mode 100644 src/openai/types/beta/beta_response_image_gen_call_completed_event.py create mode 100644 src/openai/types/beta/beta_response_image_gen_call_generating_event.py create mode 100644 src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py create mode 100644 src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py create mode 100644 src/openai/types/beta/beta_response_in_progress_event.py create mode 100644 src/openai/types/beta/beta_response_includable.py create mode 100644 src/openai/types/beta/beta_response_incomplete_event.py create mode 100644 src/openai/types/beta/beta_response_inject_created_event.py create mode 100644 src/openai/types/beta/beta_response_inject_event.py create mode 100644 src/openai/types/beta/beta_response_inject_event_param.py create mode 100644 src/openai/types/beta/beta_response_inject_failed_event.py create mode 100644 src/openai/types/beta/beta_response_input.py create mode 100644 src/openai/types/beta/beta_response_input_content.py create mode 100644 src/openai/types/beta/beta_response_input_content_param.py create mode 100644 src/openai/types/beta/beta_response_input_file.py create mode 100644 src/openai/types/beta/beta_response_input_file_content.py create mode 100644 src/openai/types/beta/beta_response_input_file_content_param.py create mode 100644 src/openai/types/beta/beta_response_input_file_param.py create mode 100644 src/openai/types/beta/beta_response_input_image.py create mode 100644 src/openai/types/beta/beta_response_input_image_content.py create mode 100644 src/openai/types/beta/beta_response_input_image_content_param.py create mode 100644 src/openai/types/beta/beta_response_input_image_param.py create mode 100644 src/openai/types/beta/beta_response_input_item.py create mode 100644 src/openai/types/beta/beta_response_input_item_param.py create mode 100644 src/openai/types/beta/beta_response_input_message_content_list.py create mode 100644 src/openai/types/beta/beta_response_input_message_content_list_param.py create mode 100644 src/openai/types/beta/beta_response_input_message_item.py create mode 100644 src/openai/types/beta/beta_response_input_param.py create mode 100644 src/openai/types/beta/beta_response_input_text.py create mode 100644 src/openai/types/beta/beta_response_input_text_content.py create mode 100644 src/openai/types/beta/beta_response_input_text_content_param.py create mode 100644 src/openai/types/beta/beta_response_input_text_param.py create mode 100644 src/openai/types/beta/beta_response_item.py create mode 100644 src/openai/types/beta/beta_response_local_environment.py create mode 100644 src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py create mode 100644 src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py create mode 100644 src/openai/types/beta/beta_response_mcp_call_completed_event.py create mode 100644 src/openai/types/beta/beta_response_mcp_call_failed_event.py create mode 100644 src/openai/types/beta/beta_response_mcp_call_in_progress_event.py create mode 100644 src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py create mode 100644 src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py create mode 100644 src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py create mode 100644 src/openai/types/beta/beta_response_output_item.py create mode 100644 src/openai/types/beta/beta_response_output_item_added_event.py create mode 100644 src/openai/types/beta/beta_response_output_item_done_event.py create mode 100644 src/openai/types/beta/beta_response_output_message.py create mode 100644 src/openai/types/beta/beta_response_output_message_param.py create mode 100644 src/openai/types/beta/beta_response_output_refusal.py create mode 100644 src/openai/types/beta/beta_response_output_refusal_param.py create mode 100644 src/openai/types/beta/beta_response_output_text.py create mode 100644 src/openai/types/beta/beta_response_output_text_annotation_added_event.py create mode 100644 src/openai/types/beta/beta_response_output_text_param.py create mode 100644 src/openai/types/beta/beta_response_prompt.py create mode 100644 src/openai/types/beta/beta_response_prompt_param.py create mode 100644 src/openai/types/beta/beta_response_queued_event.py create mode 100644 src/openai/types/beta/beta_response_reasoning_item.py create mode 100644 src/openai/types/beta/beta_response_reasoning_item_param.py create mode 100644 src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py create mode 100644 src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py create mode 100644 src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py create mode 100644 src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py create mode 100644 src/openai/types/beta/beta_response_reasoning_text_delta_event.py create mode 100644 src/openai/types/beta/beta_response_reasoning_text_done_event.py create mode 100644 src/openai/types/beta/beta_response_refusal_delta_event.py create mode 100644 src/openai/types/beta/beta_response_refusal_done_event.py create mode 100644 src/openai/types/beta/beta_response_status.py create mode 100644 src/openai/types/beta/beta_response_stream_event.py create mode 100644 src/openai/types/beta/beta_response_text_config.py create mode 100644 src/openai/types/beta/beta_response_text_config_param.py create mode 100644 src/openai/types/beta/beta_response_text_delta_event.py create mode 100644 src/openai/types/beta/beta_response_text_done_event.py create mode 100644 src/openai/types/beta/beta_response_tool_search_call.py create mode 100644 src/openai/types/beta/beta_response_tool_search_output_item.py create mode 100644 src/openai/types/beta/beta_response_tool_search_output_item_param.py create mode 100644 src/openai/types/beta/beta_response_tool_search_output_item_param_param.py create mode 100644 src/openai/types/beta/beta_response_usage.py create mode 100644 src/openai/types/beta/beta_response_web_search_call_completed_event.py create mode 100644 src/openai/types/beta/beta_response_web_search_call_in_progress_event.py create mode 100644 src/openai/types/beta/beta_response_web_search_call_searching_event.py create mode 100644 src/openai/types/beta/beta_responses_client_event.py create mode 100644 src/openai/types/beta/beta_responses_client_event_param.py create mode 100644 src/openai/types/beta/beta_responses_server_event.py create mode 100644 src/openai/types/beta/beta_skill_reference.py create mode 100644 src/openai/types/beta/beta_skill_reference_param.py create mode 100644 src/openai/types/beta/beta_tool.py create mode 100644 src/openai/types/beta/beta_tool_choice_allowed.py create mode 100644 src/openai/types/beta/beta_tool_choice_allowed_param.py create mode 100644 src/openai/types/beta/beta_tool_choice_apply_patch.py create mode 100644 src/openai/types/beta/beta_tool_choice_apply_patch_param.py create mode 100644 src/openai/types/beta/beta_tool_choice_custom.py create mode 100644 src/openai/types/beta/beta_tool_choice_custom_param.py create mode 100644 src/openai/types/beta/beta_tool_choice_function.py create mode 100644 src/openai/types/beta/beta_tool_choice_function_param.py create mode 100644 src/openai/types/beta/beta_tool_choice_mcp.py create mode 100644 src/openai/types/beta/beta_tool_choice_mcp_param.py create mode 100644 src/openai/types/beta/beta_tool_choice_options.py create mode 100644 src/openai/types/beta/beta_tool_choice_shell.py create mode 100644 src/openai/types/beta/beta_tool_choice_shell_param.py create mode 100644 src/openai/types/beta/beta_tool_choice_types.py create mode 100644 src/openai/types/beta/beta_tool_choice_types_param.py create mode 100644 src/openai/types/beta/beta_tool_param.py create mode 100644 src/openai/types/beta/beta_tool_search_tool.py create mode 100644 src/openai/types/beta/beta_tool_search_tool_param.py create mode 100644 src/openai/types/beta/beta_web_search_preview_tool.py create mode 100644 src/openai/types/beta/beta_web_search_preview_tool_param.py create mode 100644 src/openai/types/beta/beta_web_search_tool.py create mode 100644 src/openai/types/beta/beta_web_search_tool_param.py create mode 100644 src/openai/types/beta/response_compact_params.py create mode 100644 src/openai/types/beta/response_create_params.py create mode 100644 src/openai/types/beta/response_retrieve_params.py create mode 100644 src/openai/types/beta/responses/__init__.py create mode 100644 src/openai/types/beta/responses/beta_response_item_list.py create mode 100644 src/openai/types/beta/responses/input_item_list_params.py create mode 100644 src/openai/types/beta/responses/input_token_count_params.py create mode 100644 src/openai/types/beta/responses/input_token_count_response.py create mode 100644 src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py create mode 100644 tests/api_resources/beta/responses/__init__.py create mode 100644 tests/api_resources/beta/responses/test_input_items.py create mode 100644 tests/api_resources/beta/responses/test_input_tokens.py create mode 100644 tests/api_resources/beta/test_responses.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e59f0b3e3b..e39a142b9e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.44.0" + ".": "2.45.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index f6b4053c1b..24895ee467 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 264 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-b5b621065906a2579dc180db1236ee3b08a4fca9539accc2fbbf88da0ca3923f.yml -openapi_spec_hash: 45b1b4692b26e714008d8120ccfc7433 -config_hash: ef3ce17315a31703e7af0567b3e9738c +configured_endpoints: 271 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-356010b9b9fd6228b457b8fcfa376cf4928a8f3bd4728e7ba5e4b6b5ef4f5843.yml +openapi_spec_hash: 885864ae98a443166f585f856c464fb2 +config_hash: 1f1e3b4050e2cb4bc780ce0b70e2b3e6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b063cfc7..32b95dda50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 2.45.0 (2026-07-09) + +Full Changelog: [v2.44.0...v2.45.0](https://github.com/openai/openai-python/compare/v2.44.0...v2.45.0) + +### Features + +* **api:** gpt-5.6-sol updates ([039d1fe](https://github.com/openai/openai-python/commit/039d1feb264a2dca7195ba5028e9fb47a5e04987)) + + +### Bug Fixes + +* **api:** restore beta resource accessors ([2dfc130](https://github.com/openai/openai-python/commit/2dfc130b8f0fdb0049e075aac21aaef29482b4e3)) + + +### Chores + +* retrigger release automation ([7b61351](https://github.com/openai/openai-python/commit/7b61351b014bb6ca4623ff6cce7f32f45038a92e)) + ## 2.44.0 (2026-06-24) Full Changelog: [v2.43.0...v2.44.0](https://github.com/openai/openai-python/compare/v2.43.0...v2.44.0) diff --git a/api.md b/api.md index 56e01254dc..8b44de0b8b 100644 --- a/api.md +++ b/api.md @@ -431,6 +431,197 @@ Methods: # Beta +## Responses + +Types: + +```python +from openai.types.beta import ( + BetaApplyPatchTool, + BetaCompactedResponse, + BetaComputerAction, + BetaComputerActionList, + BetaComputerTool, + BetaComputerUsePreviewTool, + BetaContainerAuto, + BetaContainerNetworkPolicyAllowlist, + BetaContainerNetworkPolicyDisabled, + BetaContainerNetworkPolicyDomainSecret, + BetaContainerReference, + BetaCustomTool, + BetaEasyInputMessage, + BetaFileSearchTool, + BetaFunctionShellTool, + BetaFunctionTool, + BetaInlineSkill, + BetaInlineSkillSource, + BetaLocalEnvironment, + BetaLocalSkill, + BetaNamespaceTool, + BetaResponse, + BetaResponseApplyPatchToolCall, + BetaResponseApplyPatchToolCallOutput, + BetaResponseAudioDeltaEvent, + BetaResponseAudioDoneEvent, + BetaResponseAudioTranscriptDeltaEvent, + BetaResponseAudioTranscriptDoneEvent, + BetaResponseCodeInterpreterCallCodeDeltaEvent, + BetaResponseCodeInterpreterCallCodeDoneEvent, + BetaResponseCodeInterpreterCallCompletedEvent, + BetaResponseCodeInterpreterCallInProgressEvent, + BetaResponseCodeInterpreterCallInterpretingEvent, + BetaResponseCodeInterpreterToolCall, + BetaResponseCompactionItem, + BetaResponseCompactionItemParam, + BetaResponseCompletedEvent, + BetaResponseComputerToolCall, + BetaResponseComputerToolCallOutputItem, + BetaResponseComputerToolCallOutputScreenshot, + BetaResponseContainerReference, + BetaResponseContent, + BetaResponseContentPartAddedEvent, + BetaResponseContentPartDoneEvent, + BetaResponseConversationParam, + BetaResponseCreatedEvent, + BetaResponseCustomToolCall, + BetaResponseCustomToolCallInputDeltaEvent, + BetaResponseCustomToolCallInputDoneEvent, + BetaResponseCustomToolCallItem, + BetaResponseCustomToolCallOutput, + BetaResponseCustomToolCallOutputItem, + BetaResponseError, + BetaResponseErrorEvent, + BetaResponseFailedEvent, + BetaResponseFileSearchCallCompletedEvent, + BetaResponseFileSearchCallInProgressEvent, + BetaResponseFileSearchCallSearchingEvent, + BetaResponseFileSearchToolCall, + BetaResponseFormatTextConfig, + BetaResponseFormatTextJSONSchemaConfig, + BetaResponseFunctionCallArgumentsDeltaEvent, + BetaResponseFunctionCallArgumentsDoneEvent, + BetaResponseFunctionCallOutputItem, + BetaResponseFunctionCallOutputItemList, + BetaResponseFunctionShellCallOutputContent, + BetaResponseFunctionShellToolCall, + BetaResponseFunctionShellToolCallOutput, + BetaResponseFunctionToolCall, + BetaResponseFunctionToolCallItem, + BetaResponseFunctionToolCallOutputItem, + BetaResponseFunctionWebSearch, + BetaResponseImageGenCallCompletedEvent, + BetaResponseImageGenCallGeneratingEvent, + BetaResponseImageGenCallInProgressEvent, + BetaResponseImageGenCallPartialImageEvent, + BetaResponseInProgressEvent, + BetaResponseIncludable, + BetaResponseIncompleteEvent, + BetaResponseInjectCreatedEvent, + BetaResponseInjectEvent, + BetaResponseInjectFailedEvent, + BetaResponseInput, + BetaResponseInputAudio, + BetaResponseInputContent, + BetaResponseInputFile, + BetaResponseInputFileContent, + BetaResponseInputImage, + BetaResponseInputImageContent, + BetaResponseInputItem, + BetaResponseInputMessageContentList, + BetaResponseInputMessageItem, + BetaResponseInputText, + BetaResponseInputTextContent, + BetaResponseItem, + BetaResponseLocalEnvironment, + BetaResponseMcpCallArgumentsDeltaEvent, + BetaResponseMcpCallArgumentsDoneEvent, + BetaResponseMcpCallCompletedEvent, + BetaResponseMcpCallFailedEvent, + BetaResponseMcpCallInProgressEvent, + BetaResponseMcpListToolsCompletedEvent, + BetaResponseMcpListToolsFailedEvent, + BetaResponseMcpListToolsInProgressEvent, + BetaResponseOutputAudio, + BetaResponseOutputItem, + BetaResponseOutputItemAddedEvent, + BetaResponseOutputItemDoneEvent, + BetaResponseOutputMessage, + BetaResponseOutputRefusal, + BetaResponseOutputText, + BetaResponseOutputTextAnnotationAddedEvent, + BetaResponsePrompt, + BetaResponseQueuedEvent, + BetaResponseReasoningItem, + BetaResponseReasoningSummaryPartAddedEvent, + BetaResponseReasoningSummaryPartDoneEvent, + BetaResponseReasoningSummaryTextDeltaEvent, + BetaResponseReasoningSummaryTextDoneEvent, + BetaResponseReasoningTextDeltaEvent, + BetaResponseReasoningTextDoneEvent, + BetaResponseRefusalDeltaEvent, + BetaResponseRefusalDoneEvent, + BetaResponseStatus, + BetaResponseStreamEvent, + BetaResponseTextConfig, + BetaResponseTextDeltaEvent, + BetaResponseTextDoneEvent, + BetaResponseToolSearchCall, + BetaResponseToolSearchOutputItem, + BetaResponseToolSearchOutputItemParam, + BetaResponseUsage, + BetaResponseWebSearchCallCompletedEvent, + BetaResponseWebSearchCallInProgressEvent, + BetaResponseWebSearchCallSearchingEvent, + BetaResponsesClientEvent, + BetaResponsesServerEvent, + BetaSkillReference, + BetaTool, + BetaToolChoiceAllowed, + BetaToolChoiceApplyPatch, + BetaToolChoiceCustom, + BetaToolChoiceFunction, + BetaToolChoiceMcp, + BetaToolChoiceOptions, + BetaToolChoiceShell, + BetaToolChoiceTypes, + BetaToolSearchTool, + BetaWebSearchPreviewTool, + BetaWebSearchTool, +) +``` + +Methods: + +- client.beta.responses.create(\*\*params) -> BetaResponse +- client.beta.responses.retrieve(response_id, \*\*params) -> BetaResponse +- client.beta.responses.delete(response_id) -> None +- client.beta.responses.cancel(response_id) -> BetaResponse +- client.beta.responses.compact(\*\*params) -> BetaCompactedResponse + +### InputItems + +Types: + +```python +from openai.types.beta.responses import BetaResponseItemList +``` + +Methods: + +- client.beta.responses.input_items.list(response_id, \*\*params) -> SyncCursorPage[BetaResponseItem] + +### InputTokens + +Types: + +```python +from openai.types.beta.responses import InputTokenCountResponse +``` + +Methods: + +- client.beta.responses.input_tokens.count(\*\*params) -> InputTokenCountResponse + ## Realtime Types: diff --git a/examples/responses/multi_agent_streaming.py b/examples/responses/multi_agent_streaming.py new file mode 100644 index 0000000000..5933e75357 --- /dev/null +++ b/examples/responses/multi_agent_streaming.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from openai import OpenAI +from openai.types.beta import BetaResponseOutputItem + +PROMPT = """Compare these fictional proposals on speed, cost, reliability, and security. +Proposal Alpha: 2-week launch, $40k, managed vendor, 99.9% SLA, data leaves the VPC. +Proposal Beta: 6-week launch, $70k, self-hosted, 99.5% target, data stays in the VPC. +Delegate each proposal to a separate agent, then compare their evidence and recommend one.""" + + +def agent_name(item: BetaResponseOutputItem) -> str: + return item.agent.agent_name if item.agent else "/root" + + +client = OpenAI() + +stream = client.beta.responses.create( + model="gpt-5.6-sol", + input=PROMPT, + multi_agent={"enabled": True}, + stream=True, + betas=["responses_multi_agent=v1"], +) + +item_agents: dict[int, str] = {} +labeled_items: set[int] = set() +for event in stream: + if event.type == "response.output_item.added": + item_agents[event.output_index] = agent_name(event.item) + elif event.type == "response.output_text.delta": + name = item_agents.get(event.output_index, "/root") + if event.output_index not in labeled_items: + separator = "\n\n" if labeled_items else "" + role = "Coordinator" if name == "/root" else "Agent" + print(f"{separator}━━━ {role}: {name} ━━━\n") + labeled_items.add(event.output_index) + print(event.delta, end="", flush=True) + +print() diff --git a/examples/responses/multi_agent_websocket.py b/examples/responses/multi_agent_websocket.py new file mode 100644 index 0000000000..a6390419de --- /dev/null +++ b/examples/responses/multi_agent_websocket.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from openai import OpenAI +from openai.types.beta import BetaResponseOutputItem + +PROMPT = """Compare these fictional proposals on speed, cost, reliability, and security. +Proposal Alpha: 2-week launch, $40k, managed vendor, 99.9% SLA, data leaves the VPC. +Proposal Beta: 6-week launch, $70k, self-hosted, 99.5% target, data stays in the VPC. +Delegate each proposal to a separate agent, then compare their evidence and recommend one.""" + + +def agent_name(item: BetaResponseOutputItem) -> str: + return item.agent.agent_name if item.agent else "/root" + + +client = OpenAI() + +with client.beta.responses.connect( + extra_headers={"OpenAI-Beta": "responses_multi_agent=v1"}, +) as connection: + connection.send( + { + "type": "response.create", + "model": "gpt-5.6-sol", + "input": PROMPT, + "multi_agent": {"enabled": True}, + } + ) + + item_agents: dict[int, str] = {} + labeled_items: set[int] = set() + for event in connection: + if event.type == "response.output_item.added": + item_agents[event.output_index] = agent_name(event.item) + elif event.type == "response.output_text.delta": + name = item_agents.get(event.output_index, "/root") + if event.output_index not in labeled_items: + separator = "\n\n" if labeled_items else "" + role = "Coordinator" if name == "/root" else "Agent" + print(f"{separator}━━━ {role}: {name} ━━━\n") + labeled_items.add(event.output_index) + print(event.delta, end="", flush=True) + if event.type == "response.completed": + break + +print() diff --git a/pyproject.toml b/pyproject.toml index c63c46452e..423289c26e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.44.0" +version = "2.45.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index e27a90e2a8..b9f16d5834 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.44.0" # x-release-please-version +__version__ = "2.45.0" # x-release-please-version diff --git a/src/openai/lib/_parsing/_responses.py b/src/openai/lib/_parsing/_responses.py index 232718cef6..c607587ec1 100644 --- a/src/openai/lib/_parsing/_responses.py +++ b/src/openai/lib/_parsing/_responses.py @@ -105,6 +105,8 @@ def parse_response( or output.type == "tool_search_output" or output.type == "additional_tools" or output.type == "reasoning" + or output.type == "program" + or output.type == "program_output" or output.type == "compaction" or output.type == "mcp_call" or output.type == "mcp_approval_request" diff --git a/src/openai/resources/beta/__init__.py b/src/openai/resources/beta/__init__.py index 6d6f538670..754860bad3 100644 --- a/src/openai/resources/beta/__init__.py +++ b/src/openai/resources/beta/__init__.py @@ -24,6 +24,14 @@ ThreadsWithStreamingResponse, AsyncThreadsWithStreamingResponse, ) +from .responses import ( + Responses, + AsyncResponses, + ResponsesWithRawResponse, + AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse, +) from .assistants import ( Assistants, AsyncAssistants, @@ -34,6 +42,12 @@ ) __all__ = [ + "Responses", + "AsyncResponses", + "ResponsesWithRawResponse", + "AsyncResponsesWithRawResponse", + "ResponsesWithStreamingResponse", + "AsyncResponsesWithStreamingResponse", "ChatKit", "AsyncChatKit", "ChatKitWithRawResponse", diff --git a/src/openai/resources/beta/assistants.py b/src/openai/resources/beta/assistants.py index 6301082fea..5f2197be46 100644 --- a/src/openai/resources/beta/assistants.py +++ b/src/openai/resources/beta/assistants.py @@ -100,19 +100,12 @@ def create( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -325,19 +318,12 @@ def update( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -593,19 +579,12 @@ async def create( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -818,19 +797,12 @@ async def update( name: The name of the assistant. The maximum length is 256 characters. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/beta/beta.py b/src/openai/resources/beta/beta.py index 388a1c5d1f..807215de65 100644 --- a/src/openai/resources/beta/beta.py +++ b/src/openai/resources/beta/beta.py @@ -33,6 +33,14 @@ Realtime, AsyncRealtime, ) +from .responses.responses import ( + Responses, + AsyncResponses, + ResponsesWithRawResponse, + AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse, +) __all__ = ["Beta", "AsyncBeta"] @@ -46,6 +54,10 @@ def chat(self) -> Chat: def realtime(self) -> Realtime: return Realtime(self._client) + @cached_property + def responses(self) -> Responses: + return Responses(self._client) + @cached_property def chatkit(self) -> ChatKit: return ChatKit(self._client) @@ -89,6 +101,10 @@ def chat(self) -> AsyncChat: def realtime(self) -> AsyncRealtime: return AsyncRealtime(self._client) + @cached_property + def responses(self) -> AsyncResponses: + return AsyncResponses(self._client) + @cached_property def chatkit(self) -> AsyncChatKit: return AsyncChatKit(self._client) @@ -127,6 +143,10 @@ class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def responses(self) -> ResponsesWithRawResponse: + return ResponsesWithRawResponse(self._beta.responses) + @cached_property def chatkit(self) -> ChatKitWithRawResponse: return ChatKitWithRawResponse(self._beta.chatkit) @@ -146,6 +166,10 @@ class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def responses(self) -> AsyncResponsesWithRawResponse: + return AsyncResponsesWithRawResponse(self._beta.responses) + @cached_property def chatkit(self) -> AsyncChatKitWithRawResponse: return AsyncChatKitWithRawResponse(self._beta.chatkit) @@ -165,6 +189,10 @@ class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta + @cached_property + def responses(self) -> ResponsesWithStreamingResponse: + return ResponsesWithStreamingResponse(self._beta.responses) + @cached_property def chatkit(self) -> ChatKitWithStreamingResponse: return ChatKitWithStreamingResponse(self._beta.chatkit) @@ -184,6 +212,10 @@ class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta + @cached_property + def responses(self) -> AsyncResponsesWithStreamingResponse: + return AsyncResponsesWithStreamingResponse(self._beta.responses) + @cached_property def chatkit(self) -> AsyncChatKitWithStreamingResponse: return AsyncChatKitWithStreamingResponse(self._beta.chatkit) diff --git a/src/openai/resources/beta/responses/__init__.py b/src/openai/resources/beta/responses/__init__.py new file mode 100644 index 0000000000..51d318ad8d --- /dev/null +++ b/src/openai/resources/beta/responses/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .responses import ( + Responses, + AsyncResponses, + ResponsesWithRawResponse, + AsyncResponsesWithRawResponse, + ResponsesWithStreamingResponse, + AsyncResponsesWithStreamingResponse, +) +from .input_items import ( + InputItems, + AsyncInputItems, + InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse, +) +from .input_tokens import ( + InputTokens, + AsyncInputTokens, + InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse, +) + +__all__ = [ + "InputItems", + "AsyncInputItems", + "InputItemsWithRawResponse", + "AsyncInputItemsWithRawResponse", + "InputItemsWithStreamingResponse", + "AsyncInputItemsWithStreamingResponse", + "InputTokens", + "AsyncInputTokens", + "InputTokensWithRawResponse", + "AsyncInputTokensWithRawResponse", + "InputTokensWithStreamingResponse", + "AsyncInputTokensWithStreamingResponse", + "Responses", + "AsyncResponses", + "ResponsesWithRawResponse", + "AsyncResponsesWithRawResponse", + "ResponsesWithStreamingResponse", + "AsyncResponsesWithStreamingResponse", +] diff --git a/src/openai/resources/beta/responses/input_items.py b/src/openai/resources/beta/responses/input_items.py new file mode 100644 index 0000000000..90ea3c1ce8 --- /dev/null +++ b/src/openai/resources/beta/responses/input_items.py @@ -0,0 +1,238 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Any, List, cast +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import is_given, path_template, maybe_transform, strip_not_given +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....pagination import SyncCursorPage, AsyncCursorPage +from ...._base_client import AsyncPaginator, make_request_options +from ....types.beta.responses import input_item_list_params +from ....types.beta.beta_response_item import BetaResponseItem +from ....types.beta.beta_response_includable import BetaResponseIncludable + +__all__ = ["InputItems", "AsyncInputItems"] + + +class InputItems(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | Omit = omit, + include: List[BetaResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncCursorPage[BetaResponseItem]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._get_api_list( + path_template("/responses/{response_id}/input_items?beta=true", response_id=response_id), + page=SyncCursorPage[BetaResponseItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, BetaResponseItem), # Union types cannot be passed in as arguments in the type system + ) + + +class AsyncInputItems(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInputItemsWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputItemsWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputItemsWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputItemsWithStreamingResponse(self) + + def list( + self, + response_id: str, + *, + after: str | Omit = omit, + include: List[BetaResponseIncludable] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[BetaResponseItem, AsyncCursorPage[BetaResponseItem]]: + """ + Returns a list of input items for a given response. + + Args: + after: An item ID to list items after, used in pagination. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the default is 20. + + order: The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._get_api_list( + path_template("/responses/{response_id}/input_items?beta=true", response_id=response_id), + page=AsyncCursorPage[BetaResponseItem], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "after": after, + "include": include, + "limit": limit, + "order": order, + }, + input_item_list_params.InputItemListParams, + ), + security={"bearer_auth": True}, + ), + model=cast(Any, BetaResponseItem), # Union types cannot be passed in as arguments in the type system + ) + + +class InputItemsWithRawResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.to_raw_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithRawResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = _legacy_response.async_to_raw_response_wrapper( + input_items.list, + ) + + +class InputItemsWithStreamingResponse: + def __init__(self, input_items: InputItems) -> None: + self._input_items = input_items + + self.list = to_streamed_response_wrapper( + input_items.list, + ) + + +class AsyncInputItemsWithStreamingResponse: + def __init__(self, input_items: AsyncInputItems) -> None: + self._input_items = input_items + + self.list = async_to_streamed_response_wrapper( + input_items.list, + ) diff --git a/src/openai/resources/beta/responses/input_tokens.py b/src/openai/resources/beta/responses/input_tokens.py new file mode 100644 index 0000000000..4708ba0e35 --- /dev/null +++ b/src/openai/resources/beta/responses/input_tokens.py @@ -0,0 +1,340 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ...._utils import is_given, maybe_transform, strip_not_given, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.beta.responses import input_token_count_params +from ....types.beta.beta_tool_param import BetaToolParam +from ....types.beta.beta_response_input_item_param import BetaResponseInputItemParam +from ....types.beta.responses.input_token_count_response import InputTokenCountResponse + +__all__ = ["InputTokens", "AsyncInputTokens"] + + +class InputTokens(SyncAPIResource): + @cached_property + def with_raw_response(self) -> InputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return InputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> InputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return InputTokensWithStreamingResponse(self) + + def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[input_token_count_params.Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[BetaToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: Controls which tool the model should use, if any. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + "/responses/input_tokens?beta=true", + body=maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "personality": personality, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=InputTokenCountResponse, + ) + + +class AsyncInputTokens(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncInputTokensWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncInputTokensWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncInputTokensWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncInputTokensWithStreamingResponse(self) + + async def count( + self, + *, + conversation: Optional[input_token_count_params.Conversation] | Omit = omit, + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + model: Optional[str] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + personality: Union[str, Literal["friendly", "pragmatic"]] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + reasoning: Optional[input_token_count_params.Reasoning] | Omit = omit, + text: Optional[input_token_count_params.Text] | Omit = omit, + tool_choice: Optional[input_token_count_params.ToolChoice] | Omit = omit, + tools: Optional[Iterable[BetaToolParam]] | Omit = omit, + truncation: Literal["auto", "disabled"] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> InputTokenCountResponse: + """ + Returns input token counts of the request. + + Returns an object with `object` set to `response.input_tokens` and an + `input_tokens` count. + + Args: + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + personality: A model-owned style preset to apply to this request. Omit this parameter to use + the model's default style. Supported values may expand over time. Values must be + at most 64 characters. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + reasoning: **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: Controls which tool the model should use, if any. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + truncation: The truncation strategy to use for the model response. - `auto`: If the input to + this Response exceeds the model's context window size, the model will truncate + the response to fit the context window by dropping items from the beginning of + the conversation. - `disabled` (default): If the input size will exceed the + context window size for a model, the request will fail with a 400 error. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + "/responses/input_tokens?beta=true", + body=await async_maybe_transform( + { + "conversation": conversation, + "input": input, + "instructions": instructions, + "model": model, + "parallel_tool_calls": parallel_tool_calls, + "personality": personality, + "previous_response_id": previous_response_id, + "reasoning": reasoning, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "truncation": truncation, + }, + input_token_count_params.InputTokenCountParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=InputTokenCountResponse, + ) + + +class InputTokensWithRawResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.to_raw_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithRawResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = _legacy_response.async_to_raw_response_wrapper( + input_tokens.count, + ) + + +class InputTokensWithStreamingResponse: + def __init__(self, input_tokens: InputTokens) -> None: + self._input_tokens = input_tokens + + self.count = to_streamed_response_wrapper( + input_tokens.count, + ) + + +class AsyncInputTokensWithStreamingResponse: + def __init__(self, input_tokens: AsyncInputTokens) -> None: + self._input_tokens = input_tokens + + self.count = async_to_streamed_response_wrapper( + input_tokens.count, + ) diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py new file mode 100644 index 0000000000..5ac018f744 --- /dev/null +++ b/src/openai/resources/beta/responses/responses.py @@ -0,0 +1,5262 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import json +import time +import random +import logging +from types import TracebackType +from typing import TYPE_CHECKING, Any, Dict, List, Union, Callable, Iterable, Iterator, Optional, Awaitable, cast +from typing_extensions import Literal, AsyncIterator, overload + +import httpx +from pydantic import BaseModel + +from .... import _legacy_response +from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform +from ...._compat import cached_property +from ...._models import construct_type_unchecked +from .input_items import ( + InputItems, + AsyncInputItems, + InputItemsWithRawResponse, + AsyncInputItemsWithRawResponse, + InputItemsWithStreamingResponse, + AsyncInputItemsWithStreamingResponse, +) +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from .input_tokens import ( + InputTokens, + AsyncInputTokens, + InputTokensWithRawResponse, + AsyncInputTokensWithRawResponse, + InputTokensWithStreamingResponse, + AsyncInputTokensWithStreamingResponse, +) +from ...._streaming import Stream, AsyncStream +from ....types.beta import ( + response_create_params, + response_compact_params, + response_retrieve_params, + beta_responses_client_event_param, +) +from ...._exceptions import OpenAIError, WebSocketConnectionClosedError +from ...._send_queue import SendQueue +from ...._base_client import _merge_mappings, make_request_options +from ...._event_handler import EventHandlerRegistry +from ....types.beta.beta_response import BetaResponse +from ....types.beta.beta_tool_param import BetaToolParam +from ....types.websocket_reconnection import ReconnectingEvent, ReconnectingOverrides, is_recoverable_close +from ....types.beta.beta_compacted_response import BetaCompactedResponse +from ....types.websocket_connection_options import WebSocketConnectionOptions +from ....types.beta.beta_response_includable import BetaResponseIncludable +from ....types.beta.beta_response_error_event import BetaResponseErrorEvent +from ....types.beta.beta_response_input_param import BetaResponseInputParam +from ....types.beta.beta_response_prompt_param import BetaResponsePromptParam +from ....types.beta.beta_response_stream_event import BetaResponseStreamEvent +from ....types.beta.beta_responses_client_event import BetaResponsesClientEvent +from ....types.beta.beta_responses_server_event import BetaResponsesServerEvent +from ....types.beta.beta_response_input_item_param import BetaResponseInputItemParam +from ....types.beta.beta_response_text_config_param import BetaResponseTextConfigParam +from ....types.beta.beta_responses_client_event_param import BetaResponsesClientEventParam + +if TYPE_CHECKING: + from websockets.sync.client import ClientConnection as WebSocketConnection + from websockets.asyncio.client import ClientConnection as AsyncWebSocketConnection + + from ...._client import OpenAI, AsyncOpenAI + +__all__ = ["Responses", "AsyncResponses"] + +log: logging.Logger = logging.getLogger(__name__) + + +class Responses(SyncAPIResource): + @cached_property + def input_items(self) -> InputItems: + return InputItems(self._client) + + @cached_property + def input_tokens(self) -> InputTokens: + return InputTokens(self._client) + + @cached_property + def with_raw_response(self) -> ResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return ResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return ResponsesWithStreamingResponse(self) + + @overload + def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + background: Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: **gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + stream: Literal[True], + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + background: Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: **gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + stream: bool, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + background: Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: **gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + "/responses?beta=true", + body=maybe_transform( + { + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=Stream[BetaResponseStreamEvent], + ) + + @overload + def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Stream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | Stream[BetaResponseStreamEvent]: + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._get( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "include": include, + "include_obfuscation": include_obfuscation, + "starting_after": starting_after, + "stream": stream, + }, + response_retrieve_params.ResponseRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=Stream[BetaResponseStreamEvent], + ) + + def delete( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._delete( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + def cancel( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Cancels a model response with the given ID. + + Only responses created with the + `background` parameter set to `true` can be cancelled. + [Learn more](https://platform.openai.com/docs/guides/background). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + path_template("/responses/{response_id}/cancel?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + ) + + def compact( + self, + *, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ], + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority"]] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaCompactedResponse: + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced). + + Args: + model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + + service_tier: The service tier to use for this request. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return self._post( + "/responses/compact?beta=true", + body=maybe_transform( + { + "model": model, + "input": input, + "instructions": instructions, + "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaCompactedResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> ResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events and receive response stream events over the socket. + """ + return ResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class AsyncResponses(AsyncAPIResource): + @cached_property + def input_items(self) -> AsyncInputItems: + return AsyncInputItems(self._client) + + @cached_property + def input_tokens(self) -> AsyncInputTokens: + return AsyncInputTokens(self._client) + + @cached_property + def with_raw_response(self) -> AsyncResponsesWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncResponsesWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncResponsesWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncResponsesWithStreamingResponse(self) + + @overload + async def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + background: Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: **gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + stream: Literal[True], + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + background: Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: **gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + stream: bool, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + """Creates a model response. + + Provide + [text](https://platform.openai.com/docs/guides/text) or + [image](https://platform.openai.com/docs/guides/images) inputs to generate + [text](https://platform.openai.com/docs/guides/text) or + [JSON](https://platform.openai.com/docs/guides/structured-outputs) outputs. Have + the model call your own + [custom code](https://platform.openai.com/docs/guides/function-calling) or use + built-in [tools](https://platform.openai.com/docs/guides/tools) like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search) to use + your own data as input for the model's response. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + background: Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + + context_management: Context management configuration for this request. + + conversation: The conversation that this response belongs to. Items from this conversation are + prepended to `input_items` for this response request. Input items and output + items from this response are automatically added to this conversation after this + response completes. + + include: Specify additional output data to include in the model response. Currently + supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + + input: Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + + instructions: A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + + max_output_tokens: An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + + max_tool_calls: The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + + metadata: Set of 16 key-value pairs that can be attached to an object. This can be useful + for storing additional information about the object in a structured format, and + querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + + model: Model ID used to generate the response, like `gpt-4o` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + moderation: Configuration for running moderation on the input and output of this response. + + multi_agent: Configuration for server-hosted multi-agent execution. + + parallel_tool_calls: Whether to allow the model to run tool calls in parallel. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt: Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + + prompt_cache_key: Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + + reasoning: **gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + + safety_identifier: A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + service_tier: Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + + store: Whether to store the generated model response for later retrieval via API. + + stream_options: Options for streaming responses. Only set this when you set `stream: true`. + + temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will + make the output more random, while lower values like 0.2 will make it more + focused and deterministic. We generally recommend altering this or `top_p` but + not both. + + text: Configuration options for a text response from the model. Can be plain text or + structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + + tool_choice: How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + + tools: An array of tools the model may call while generating a response. You can + specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + + top_logprobs: An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + + top_p: An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + + truncation: The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + + user: This field is being replaced by `safety_identifier` and `prompt_cache_key`. Use + `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + async def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[response_create_params.ContextManagement]] | Omit = omit, + conversation: Optional[response_create_params.Conversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[response_create_params.Moderation] | Omit = omit, + multi_agent: Optional[response_create_params.MultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[response_create_params.Reasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[Literal[False]] | Literal[True] | Omit = omit, + stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: response_create_params.ToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + "/responses?beta=true", + body=await async_maybe_transform( + { + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + }, + response_create_params.ResponseCreateParamsStreaming + if stream + else response_create_params.ResponseCreateParamsNonStreaming, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=AsyncStream[BetaResponseStreamEvent], + ) + + @overload + async def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """ + Retrieves a model response with the given ID. + + Args: + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: Literal[True], + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncStream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def retrieve( + self, + response_id: str, + *, + stream: bool, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + """ + Retrieves a model response with the given ID. + + Args: + stream: If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + + include: Additional fields to include in the response. See the `include` parameter for + Response creation above for more information. + + include_obfuscation: When true, stream obfuscation will be enabled. Stream obfuscation adds random + characters to an `obfuscation` field on streaming delta events to normalize + payload sizes as a mitigation to certain side-channel attacks. These obfuscation + fields are included by default, but add a small amount of overhead to the data + stream. You can set `include_obfuscation` to false to optimize for bandwidth if + you trust the network links between your application and the OpenAI API. + + starting_after: The sequence number of the event after which to start streaming. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + async def retrieve( + self, + response_id: str, + *, + include: List[BetaResponseIncludable] | Omit = omit, + include_obfuscation: bool | Omit = omit, + starting_after: int | Omit = omit, + stream: Literal[False] | Literal[True] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse | AsyncStream[BetaResponseStreamEvent]: + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._get( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "include": include, + "include_obfuscation": include_obfuscation, + "starting_after": starting_after, + "stream": stream, + }, + response_retrieve_params.ResponseRetrieveParams, + ), + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + stream=stream or False, + stream_cls=AsyncStream[BetaResponseStreamEvent], + ) + + async def delete( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Deletes a model response with the given ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._delete( + path_template("/responses/{response_id}?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=NoneType, + ) + + async def cancel( + self, + response_id: str, + *, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaResponse: + """Cancels a model response with the given ID. + + Only responses created with the + `background` parameter set to `true` can be cancelled. + [Learn more](https://platform.openai.com/docs/guides/background). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not response_id: + raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}") + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + path_template("/responses/{response_id}/cancel?beta=true", response_id=response_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaResponse, + ) + + async def compact( + self, + *, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ], + input: Union[str, Iterable[BetaResponseInputItemParam], None] | Omit = omit, + instructions: Optional[str] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "priority"]] | Omit = omit, + betas: List[Literal["responses_multi_agent=v1"]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> BetaCompactedResponse: + """Compact a conversation. + + Returns a compacted response object. + + Learn when and how to compact long-running conversations in the + [conversation state guide](https://platform.openai.com/docs/guides/conversation-state#managing-the-context-window). + For ZDR-compatible compaction details, see + [Compaction (advanced)](https://platform.openai.com/docs/guides/conversation-state#compaction-advanced). + + Args: + model: Model ID used to generate the response, like `gpt-5` or `o3`. OpenAI offers a + wide range of models with different capabilities, performance characteristics, + and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + + input: Text, image, or file inputs to the model, used to generate a response + + instructions: A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + + previous_response_id: The unique ID of the previous response to the model. Use this to create + multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + + prompt_cache_key: A key to use when reading from or writing to the prompt cache. + + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: How long to retain a prompt cache entry created by this request. + + service_tier: The service tier to use for this request. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = { + **strip_not_given({"openai-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), + **(extra_headers or {}), + } + return await self._post( + "/responses/compact?beta=true", + body=await async_maybe_transform( + { + "model": model, + "input": input, + "instructions": instructions, + "previous_response_id": previous_response_id, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "service_tier": service_tier, + }, + response_compact_params.ResponseCompactParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"bearer_auth": True}, + ), + cast_to=BetaCompactedResponse, + ) + + def connect( + self, + extra_query: Query = {}, + extra_headers: Headers = {}, + websocket_connection_options: WebSocketConnectionOptions = {}, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> AsyncResponsesConnectionManager: + """Connect to a persistent Responses API WebSocket. + + Send `response.create` events and receive response stream events over the socket. + """ + return AsyncResponsesConnectionManager( + client=self._client, + extra_query=extra_query, + extra_headers=extra_headers, + websocket_connection_options=websocket_connection_options, + on_reconnecting=on_reconnecting, + max_retries=max_retries, + initial_delay=initial_delay, + max_delay=max_delay, + max_queue_size=max_queue_size, + ) + + +class ResponsesWithRawResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = _legacy_response.to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + responses.delete, + ) + self.cancel = _legacy_response.to_raw_response_wrapper( + responses.cancel, + ) + self.compact = _legacy_response.to_raw_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> InputItemsWithRawResponse: + return InputItemsWithRawResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> InputTokensWithRawResponse: + return InputTokensWithRawResponse(self._responses.input_tokens) + + +class AsyncResponsesWithRawResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = _legacy_response.async_to_raw_response_wrapper( + responses.create, + ) + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + responses.retrieve, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + responses.delete, + ) + self.cancel = _legacy_response.async_to_raw_response_wrapper( + responses.cancel, + ) + self.compact = _legacy_response.async_to_raw_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithRawResponse: + return AsyncInputItemsWithRawResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> AsyncInputTokensWithRawResponse: + return AsyncInputTokensWithRawResponse(self._responses.input_tokens) + + +class ResponsesWithStreamingResponse: + def __init__(self, responses: Responses) -> None: + self._responses = responses + + self.create = to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = to_streamed_response_wrapper( + responses.delete, + ) + self.cancel = to_streamed_response_wrapper( + responses.cancel, + ) + self.compact = to_streamed_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> InputItemsWithStreamingResponse: + return InputItemsWithStreamingResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> InputTokensWithStreamingResponse: + return InputTokensWithStreamingResponse(self._responses.input_tokens) + + +class AsyncResponsesWithStreamingResponse: + def __init__(self, responses: AsyncResponses) -> None: + self._responses = responses + + self.create = async_to_streamed_response_wrapper( + responses.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + responses.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + responses.delete, + ) + self.cancel = async_to_streamed_response_wrapper( + responses.cancel, + ) + self.compact = async_to_streamed_response_wrapper( + responses.compact, + ) + + @cached_property + def input_items(self) -> AsyncInputItemsWithStreamingResponse: + return AsyncInputItemsWithStreamingResponse(self._responses.input_items) + + @cached_property + def input_tokens(self) -> AsyncInputTokensWithStreamingResponse: + return AsyncInputTokensWithStreamingResponse(self._responses.input_tokens) + + +class AsyncResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: AsyncResponsesResponseResource + + _connection: AsyncWebSocketConnection + + def __init__( + self, + connection: AsyncWebSocketConnection, + *, + make_ws: Callable[[Query, Headers], Awaitable[AsyncWebSocketConnection]] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=False) + + self.response = AsyncResponsesResponseResource(self) + + async def __aiter__(self) -> AsyncIterator[BetaResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield await self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not await self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + async def recv(self) -> BetaResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `BetaResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(await self.recv_bytes()) + + async def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `BetaResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = await self._connection.recv(decode=False) + log.debug(f"Received WebSocket message: %s", message) + return message + + async def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(await async_maybe_transform(event, BetaResponsesClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + await self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise + + async def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + await self._connection.send(data) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + await self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> BetaResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `BetaResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + BetaResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, BetaResponsesServerEvent)), + ) + + async def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + import asyncio + + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(1, self._max_retries + 1): + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + await asyncio.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = await self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + await self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + async def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + + async def _send(data: str) -> None: + await self._connection.send(data) + + try: + await self._send_queue.flush_async(_send) + except Exception: + log.warning("Failed to flush send queue after reconnect", exc_info=True) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.audio.delta", my_handler) + + Or as a decorator:: + + @connection.on("response.audio.delta") + async def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks until the connection is closed. This is the push-based + alternative to iterating with ``async for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + import asyncio + + async for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, BetaResponseErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + for handler in generic: + result = handler(event) + if asyncio.iscoroutine(result): + await result + + +class AsyncResponsesConnectionManager: + """ + Context manager over a `AsyncResponsesConnection` that is returned by `beta.responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + + def __init__( + self, + *, + client: AsyncOpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: AsyncResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=False) + + def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(event) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``AsyncResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> AsyncResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[AsyncResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + async def __aenter__(self) -> AsyncResponsesConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = await client.beta.responses.connect(...).enter() + # ... + await connection.close() + ``` + """ + ws = await self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = AsyncResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + await self.__connection._flush_send_queue() + + return self.__connection + + enter = __aenter__ + + async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> AsyncWebSocketConnection: + try: + from websockets.asyncio.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + return await connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + }, + extra_headers, + ), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + async def __aexit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + await self.__connection.close() + + +class ResponsesConnection: + """Represents a live WebSocket connection to the Responses API""" + + response: ResponsesResponseResource + + _connection: WebSocketConnection + + def __init__( + self, + connection: WebSocketConnection, + *, + make_ws: Callable[[Query, Headers], WebSocketConnection] | None = None, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + extra_query: Query = {}, + extra_headers: Headers = {}, + send_queue: SendQueue | None = None, + ) -> None: + self._connection = connection + self._make_ws = make_ws + self._on_reconnecting = on_reconnecting + self._max_retries = max_retries + self._initial_delay = initial_delay + self._max_delay = max_delay + self._extra_query = extra_query + self._extra_headers = extra_headers + self._intentionally_closed = False + self._is_reconnecting = False + self._send_queue = send_queue or SendQueue() + self._event_handler_registry = EventHandlerRegistry(use_lock=True) + + self.response = ResponsesResponseResource(self) + + def __iter__(self) -> Iterator[BetaResponsesServerEvent]: + """ + An infinite-iterator that will continue to yield events until + the connection is closed. + """ + from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + + while True: + try: + yield self.recv() + except ConnectionClosedOK: + return + except ConnectionClosedError as exc: + if not self._reconnect(exc): + unsent = self._send_queue.drain() + if unsent: + raise WebSocketConnectionClosedError( + "WebSocket connection closed with unsent messages", + unsent_messages=unsent, + ) from exc + raise + + def recv(self) -> BetaResponsesServerEvent: + """ + Receive the next message from the connection and parses it into a `BetaResponsesServerEvent` object. + + Canceling this method is safe. There's no risk of losing data. + """ + return self.parse_event(self.recv_bytes()) + + def recv_bytes(self) -> bytes: + """Receive the next message from the connection as raw bytes. + + Canceling this method is safe. There's no risk of losing data. + + If you want to parse the message into a `BetaResponsesServerEvent` object like `.recv()` does, + then you can call `.parse_event(data)`. + """ + message = self._connection.recv(decode=False) + log.debug(f"Received WebSocket message: %s", message) + return message + + def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(maybe_transform(event, BetaResponsesClientEventParam)) + ) + if self._is_reconnecting: + self._send_queue.enqueue(data) + return + try: + self._connection.send(data) + except Exception: + self._send_queue.enqueue(data) + raise + + def send_raw(self, data: bytes | str) -> None: + if self._is_reconnecting: + raw = data if isinstance(data, str) else data.decode("utf-8") + self._send_queue.enqueue(raw) + return + self._connection.send(data) + + def close(self, *, code: int = 1000, reason: str = "") -> None: + self._intentionally_closed = True + self._connection.close(code=code, reason=reason) + + def parse_event(self, data: str | bytes) -> BetaResponsesServerEvent: + """ + Converts a raw `str` or `bytes` message into a `BetaResponsesServerEvent` object. + + This is helpful if you're using `.recv_bytes()`. + """ + return cast( + BetaResponsesServerEvent, + construct_type_unchecked(value=json.loads(data), type_=cast(Any, BetaResponsesServerEvent)), + ) + + def _reconnect(self, exc: Exception) -> bool: + """Attempt to reconnect after a connection failure. + + Returns ``True`` if a new connection was established, ``False`` if the + caller should re-raise the original exception. + """ + if self._on_reconnecting is None or self._make_ws is None: + return False + + from websockets.exceptions import ConnectionClosedError + + close_code = 1006 + if isinstance(exc, ConnectionClosedError) and exc.rcvd is not None: + close_code = exc.rcvd.code + + if not is_recoverable_close(close_code): + return False + + self._is_reconnecting = True + + for attempt in range(1, self._max_retries + 1): + base_delay = min(self._initial_delay * (2 ** (attempt - 1)), self._max_delay) + jitter = 0.75 + random.random() * 0.25 + delay = base_delay * jitter + + event = ReconnectingEvent( + attempt=attempt, + max_attempts=self._max_retries, + delay=delay, + close_code=close_code, + extra_query=self._extra_query, + extra_headers=self._extra_headers, + ) + + try: + result = self._on_reconnecting(event) + except Exception: + self._is_reconnecting = False + return False + + if result is not None and result.get("abort"): + self._is_reconnecting = False + return False + + if result is not None: + if "extra_query" in result: + self._extra_query = result["extra_query"] + if "extra_headers" in result: + self._extra_headers = result["extra_headers"] + + log.info( + "Reconnecting to WebSocket API (attempt %d/%d) after %.1fs delay", + attempt, + self._max_retries, + delay, + ) + time.sleep(delay) + + if self._intentionally_closed: + self._is_reconnecting = False + return False + + try: + self._connection = self._make_ws(self._extra_query, self._extra_headers) + log.info("Reconnected to WebSocket API") + self._is_reconnecting = False + self._flush_send_queue() + return True + except Exception: + pass + + self._is_reconnecting = False + return False + + def _flush_send_queue(self) -> None: + """Send all queued messages over the current connection.""" + try: + self._send_queue.flush_sync(lambda data: self._connection.send(data)) + except Exception: + log.warning("Failed to flush send queue after reconnect", exc_info=True) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Adds the handler to the end of the handlers list for the given event type. + + No checks are made to see if the handler has already been added. Multiple calls + passing the same combination of event type and handler will result in the handler + being added, and called, multiple times. + + Can be used as a method (returns ``self`` for chaining):: + + connection.on("response.audio.delta", my_handler) + + Or as a decorator:: + + @connection.on("response.audio.delta") + def my_handler(event): ... + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnection: + """Remove a previously registered event handler.""" + self._event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnection, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler. + + Automatically removed after first invocation. + """ + if handler is not None: + self._event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self._event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def dispatch_events(self) -> None: + """Run the event loop, dispatching received events to registered handlers. + + Blocks the current thread until the connection is closed. This is the push-based + alternative to iterating with ``for event in connection``. + + If an ``"error"`` event arrives and no handler is registered for + ``"error"`` or ``"event"``, an ``OpenAIError`` is raised. + """ + for event in self: + event_type = event.type + specific = self._event_handler_registry.get_handlers(event_type) + generic = self._event_handler_registry.get_handlers("event") + + if event_type == "error" and not specific and not generic: + if isinstance(event, BetaResponseErrorEvent): + raise OpenAIError(f"WebSocket error: {event}") + + for handler in specific: + handler(event) + + for handler in generic: + handler(event) + + +class ResponsesConnectionManager: + """ + Context manager over a `ResponsesConnection` that is returned by `beta.responses.connect()` + + This context manager ensures that the connection will be closed when it exits. + + --- + + Note that if your application doesn't work well with the context manager approach then you + can call the `.enter()` method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + + def __init__( + self, + *, + client: OpenAI, + extra_query: Query, + extra_headers: Headers, + websocket_connection_options: WebSocketConnectionOptions, + on_reconnecting: Callable[[ReconnectingEvent], ReconnectingOverrides | None] | None = None, + max_retries: int = 5, + initial_delay: float = 0.5, + max_delay: float = 8.0, + max_queue_size: int = 1_048_576, + ) -> None: + self.__client = client + self.__connection: ResponsesConnection | None = None + self.__extra_query = extra_query + self.__extra_headers = extra_headers + self.__websocket_connection_options = websocket_connection_options + self.__on_reconnecting = on_reconnecting + self.__max_retries = max_retries + self.__initial_delay = initial_delay + self.__max_delay = max_delay + self.__send_queue = SendQueue(max_bytes=max_queue_size) + self.__event_handler_registry = EventHandlerRegistry(use_lock=True) + + def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) -> None: + """Queue a message to be sent when the connection is established. + + This can be called before entering the context manager. Queued messages + are automatically sent once the WebSocket connection opens. + """ + data = ( + event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) + if isinstance(event, BaseModel) + else json.dumps(event) + ) + self.__send_queue.enqueue(data) + + def on( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register an event handler before the connection is established. + + Handlers are transferred to the connection on enter. Supports the + same method and decorator forms as ``ResponsesConnection.on``. + """ + if handler is not None: + self.__event_handler_registry.add(event_type, handler) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn) + return fn + + return decorator + + def off(self, event_type: str, handler: Callable[..., Any]) -> ResponsesConnectionManager: + """Remove a previously registered event handler.""" + self.__event_handler_registry.remove(event_type, handler) + return self + + def once( + self, event_type: str, handler: Callable[..., Any] | None = None + ) -> Union[ResponsesConnectionManager, Callable[[Callable[..., Any]], Callable[..., Any]]]: + """Register a one-time event handler before the connection is established.""" + if handler is not None: + self.__event_handler_registry.add(event_type, handler, once=True) + return self + + def decorator(fn: Callable[..., Any]) -> Callable[..., Any]: + self.__event_handler_registry.add(event_type, fn, once=True) + return fn + + return decorator + + def __enter__(self) -> ResponsesConnection: + """ + If your application doesn't work well with the context manager approach then you + can call this method directly to initiate a connection. + + **Warning**: You must remember to close the connection with `.close()`. + + ```py + connection = client.beta.responses.connect(...).enter() + # ... + connection.close() + ``` + """ + ws = self._connect_ws(self.__extra_query, self.__extra_headers) + + self.__connection = ResponsesConnection( + ws, + make_ws=self._connect_ws if self.__on_reconnecting is not None else None, + on_reconnecting=self.__on_reconnecting, + max_retries=self.__max_retries, + initial_delay=self.__initial_delay, + max_delay=self.__max_delay, + extra_query=self.__extra_query, + extra_headers=self.__extra_headers, + send_queue=self.__send_queue, + ) + + self.__event_handler_registry.merge_into(self.__connection._event_handler_registry) + self.__connection._flush_send_queue() + + return self.__connection + + enter = __enter__ + + def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketConnection: + try: + from websockets.sync.client import connect + except ImportError as exc: + raise OpenAIError("You need to install `openai[realtime]` to use this method") from exc + + url = self._prepare_url().copy_with( + params={ + **self.__client.base_url.params, + **extra_query, + }, + ) + log.debug("Connecting to %s", url) + if self.__websocket_connection_options: + log.debug("Connection options: %s", self.__websocket_connection_options) + + return connect( + str(url), + user_agent_header=self.__client.user_agent, + additional_headers=_merge_mappings( + { + **self.__client.auth_headers, + }, + extra_headers, + ), + **self.__websocket_connection_options, + ) + + def _prepare_url(self) -> httpx.URL: + if self.__client.websocket_base_url is not None: + base_url = httpx.URL(self.__client.websocket_base_url) + else: + scheme = self.__client._base_url.scheme + ws_scheme = "ws" if scheme == "http" else "wss" + base_url = self.__client._base_url.copy_with(scheme=ws_scheme) + + merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/responses" + return base_url.copy_with(raw_path=merge_raw_path) + + def __exit__( + self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None + ) -> None: + if self.__connection is not None: + self.__connection.close() + + +class BaseResponsesConnectionResource: + def __init__(self, connection: ResponsesConnection) -> None: + self._connection = connection + + +class ResponsesResponseResource(BaseResponsesConnectionResource): + def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None: + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + self._connection.send({"type": "response.inject", "input": input, "response_id": response_id}) + + def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]] + | Omit = omit, + conversation: Optional[beta_responses_client_event_param.ResponseCreateConversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[beta_responses_client_event_param.ResponseCreateModeration] | Omit = omit, + multi_agent: Optional[beta_responses_client_event_param.ResponseCreateMultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: beta_responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_options: Optional[beta_responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: beta_responses_client_event_param.ResponseCreateToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + """ + self._connection.send( + cast( + BetaResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) + + +class BaseAsyncResponsesConnectionResource: + def __init__(self, connection: AsyncResponsesConnection) -> None: + self._connection = connection + + +class AsyncResponsesResponseResource(BaseAsyncResponsesConnectionResource): + async def inject(self, *, input: Iterable[BetaResponseInputItemParam], response_id: str) -> None: + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + await self._connection.send({"type": "response.inject", "input": input, "response_id": response_id}) + + async def create( + self, + *, + background: Optional[bool] | Omit = omit, + context_management: Optional[Iterable[beta_responses_client_event_param.ResponseCreateContextManagement]] + | Omit = omit, + conversation: Optional[beta_responses_client_event_param.ResponseCreateConversation] | Omit = omit, + include: Optional[List[BetaResponseIncludable]] | Omit = omit, + input: Union[str, BetaResponseInputParam] | Omit = omit, + instructions: Optional[str] | Omit = omit, + max_output_tokens: Optional[int] | Omit = omit, + max_tool_calls: Optional[int] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + | Omit = omit, + moderation: Optional[beta_responses_client_event_param.ResponseCreateModeration] | Omit = omit, + multi_agent: Optional[beta_responses_client_event_param.ResponseCreateMultiAgent] | Omit = omit, + parallel_tool_calls: Optional[bool] | Omit = omit, + previous_response_id: Optional[str] | Omit = omit, + prompt: Optional[BetaResponsePromptParam] | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_options: beta_responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, + reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit, + safety_identifier: str | Omit = omit, + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, + store: Optional[bool] | Omit = omit, + stream: Optional[bool] | Omit = omit, + stream_options: Optional[beta_responses_client_event_param.ResponseCreateStreamOptions] | Omit = omit, + temperature: Optional[float] | Omit = omit, + text: BetaResponseTextConfigParam | Omit = omit, + tool_choice: beta_responses_client_event_param.ResponseCreateToolChoice | Omit = omit, + tools: Iterable[BetaToolParam] | Omit = omit, + top_logprobs: Optional[int] | Omit = omit, + top_p: Optional[float] | Omit = omit, + truncation: Optional[Literal["auto", "disabled"]] | Omit = omit, + user: str | Omit = omit, + ) -> None: + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + """ + await self._connection.send( + cast( + BetaResponsesClientEventParam, + strip_not_given( + { + "type": "response.create", + "background": background, + "context_management": context_management, + "conversation": conversation, + "include": include, + "input": input, + "instructions": instructions, + "max_output_tokens": max_output_tokens, + "max_tool_calls": max_tool_calls, + "metadata": metadata, + "model": model, + "moderation": moderation, + "multi_agent": multi_agent, + "parallel_tool_calls": parallel_tool_calls, + "previous_response_id": previous_response_id, + "prompt": prompt, + "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, + "prompt_cache_retention": prompt_cache_retention, + "reasoning": reasoning, + "safety_identifier": safety_identifier, + "service_tier": service_tier, + "store": store, + "stream": stream, + "stream_options": stream_options, + "temperature": temperature, + "text": text, + "tool_choice": tool_choice, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "truncation": truncation, + "user": user, + } + ), + ) + ) diff --git a/src/openai/resources/beta/threads/runs/runs.py b/src/openai/resources/beta/threads/runs/runs.py index 385d4c680c..926f729882 100644 --- a/src/openai/resources/beta/threads/runs/runs.py +++ b/src/openai/resources/beta/threads/runs/runs.py @@ -171,19 +171,12 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -333,19 +326,12 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -491,19 +477,12 @@ def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1657,19 +1636,12 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1819,19 +1791,12 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), @@ -1977,19 +1942,12 @@ async def create( [parallel function calling](https://platform.openai.com/docs/guides/function-calling#configuring-parallel-function-calling) during tool use. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: Specifies the format that the model must output. Compatible with [GPT-4o](https://platform.openai.com/docs/models#gpt-4o), diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index 835adfde1c..f8e7d40772 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -110,6 +110,7 @@ def parse( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, @@ -211,6 +212,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), @@ -268,6 +270,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -420,11 +423,26 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -433,19 +451,12 @@ def create( - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: An object specifying the format that the model must output. @@ -587,6 +598,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -747,11 +759,26 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -760,19 +787,12 @@ def create( - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: An object specifying the format that the model must output. @@ -905,6 +925,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -1065,11 +1086,26 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -1078,19 +1114,12 @@ def create( - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: An object specifying the format that the model must output. @@ -1222,6 +1251,7 @@ def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -1270,6 +1300,7 @@ def create( "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": response_format, @@ -1520,6 +1551,7 @@ def stream( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, @@ -1592,6 +1624,7 @@ def stream( prediction=prediction, presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, prompt_cache_retention=prompt_cache_retention, reasoning_effort=reasoning_effort, safety_identifier=safety_identifier, @@ -1673,6 +1706,7 @@ async def parse( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, @@ -1774,6 +1808,7 @@ def parser(raw_completion: ChatCompletion) -> ParsedChatCompletion[ResponseForma "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": _type_to_response_format(response_format), @@ -1831,6 +1866,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -1983,11 +2019,26 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -1996,19 +2047,12 @@ async def create( - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: An object specifying the format that the model must output. @@ -2150,6 +2194,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -2310,11 +2355,26 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -2323,19 +2383,12 @@ async def create( - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: An object specifying the format that the model must output. @@ -2468,6 +2521,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -2628,11 +2682,26 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -2641,19 +2710,12 @@ async def create( - Organizations with ZDR enabled default to `in_memory` when `prompt_cache_retention` is not specified. - reasoning_effort: Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + reasoning_effort: Constrains effort on reasoning for reasoning models. Currently supported values + are `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. Reducing + reasoning effort can result in faster responses and fewer tokens used on + reasoning in a response. Not all reasoning models support every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. response_format: An object specifying the format that the model must output. @@ -2785,6 +2847,7 @@ async def create( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, @@ -2833,6 +2896,7 @@ async def create( "prediction": prediction, "presence_penalty": presence_penalty, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning_effort": reasoning_effort, "response_format": response_format, @@ -3083,6 +3147,7 @@ def stream( prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, safety_identifier: str | Omit = omit, @@ -3156,6 +3221,7 @@ def stream( prediction=prediction, presence_penalty=presence_penalty, prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, prompt_cache_retention=prompt_cache_retention, reasoning_effort=reasoning_effort, safety_identifier=safety_identifier, diff --git a/src/openai/resources/realtime/calls.py b/src/openai/resources/realtime/calls.py index 0674b2b010..140b3101f9 100644 --- a/src/openai/resources/realtime/calls.py +++ b/src/openai/resources/realtime/calls.py @@ -123,6 +123,8 @@ def accept( "gpt-realtime", "gpt-realtime-1.5", "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", @@ -483,6 +485,8 @@ async def accept( "gpt-realtime", "gpt-realtime-1.5", "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 5019d7e831..8131b57ca2 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -147,6 +147,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -267,11 +268,26 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -409,6 +425,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -535,11 +552,26 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -670,6 +702,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -796,11 +829,26 @@ def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -929,6 +977,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -970,6 +1019,7 @@ def create( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, @@ -1038,6 +1088,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -1080,6 +1131,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -1116,6 +1168,7 @@ def stream( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, @@ -1173,6 +1226,7 @@ def stream( previous_response_id=previous_response_id, prompt=prompt, prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, prompt_cache_retention=prompt_cache_retention, store=store, stream_options=stream_options, @@ -1194,7 +1248,12 @@ def stream( timeout=timeout, ) - return ResponseStreamManager(api_request, text_format=text_format, input_tools=tools, starting_after=None) + return ResponseStreamManager( + api_request, + text_format=text_format, + input_tools=tools, + starting_after=None, + ) else: if not is_given(response_id): raise ValueError("id must be provided when streaming an existing response") @@ -1234,6 +1293,7 @@ def parse( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -1294,6 +1354,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, @@ -1644,6 +1705,9 @@ def compact( *, model: Union[ Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano", @@ -1744,6 +1808,7 @@ def compact( instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "priority"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -1783,6 +1848,16 @@ def compact( prompt_cache_key: A key to use when reading from or writing to the prompt cache. + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + prompt_cache_retention: How long to retain a prompt cache entry created by this request. service_tier: The service tier to use for this request. @@ -1804,6 +1879,7 @@ def compact( "instructions": instructions, "previous_response_id": previous_response_id, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "service_tier": service_tier, }, @@ -1894,6 +1970,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -2014,11 +2091,26 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -2156,6 +2248,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -2282,11 +2375,26 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -2417,6 +2525,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -2543,11 +2652,26 @@ async def create( hit rates. Replaces the `user` field. [Learn more](https://platform.openai.com/docs/guides/prompt-caching). - prompt_cache_retention: The retention policy for the prompt cache. Set to `24h` to enable extended + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + + prompt_cache_retention: Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended prompt caching, which keeps cached prefixes active for longer, up to a maximum of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -2676,6 +2800,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -2717,6 +2842,7 @@ async def create( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, @@ -2785,6 +2911,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -2827,6 +2954,7 @@ def stream( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -2863,6 +2991,7 @@ def stream( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, @@ -2920,6 +3049,7 @@ def stream( previous_response_id=previous_response_id, prompt=prompt, prompt_cache_key=prompt_cache_key, + prompt_cache_options=prompt_cache_options, prompt_cache_retention=prompt_cache_retention, store=store, stream_options=stream_options, @@ -2985,6 +3115,7 @@ async def parse( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -3045,6 +3176,7 @@ def parser(raw_response: Response) -> ParsedResponse[TextFormatT]: "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, @@ -3395,6 +3527,9 @@ async def compact( *, model: Union[ Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano", @@ -3495,6 +3630,7 @@ async def compact( instructions: Optional[str] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt_cache_key: Optional[str] | Omit = omit, + prompt_cache_options: Optional[response_compact_params.PromptCacheOptions] | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "priority"]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -3534,6 +3670,16 @@ async def compact( prompt_cache_key: A key to use when reading from or writing to the prompt cache. + prompt_cache_options: Options for prompt caching. Supported for `gpt-5.6` and later models. By + default, OpenAI automatically chooses one implicit cache breakpoint. You can add + explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each + request can write up to four breakpoints. For cache matching, OpenAI considers + up to the latest 80 breakpoints in the conversation, without a content-block + lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The + `ttl` defaults to `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + prompt_cache_retention: How long to retain a prompt cache entry created by this request. service_tier: The service tier to use for this request. @@ -3555,6 +3701,7 @@ async def compact( "instructions": instructions, "previous_response_id": previous_response_id, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "service_tier": service_tier, }, @@ -4684,6 +4831,7 @@ def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: responses_client_event_param.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -4721,6 +4869,7 @@ def create( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, @@ -4766,6 +4915,7 @@ async def create( previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, prompt_cache_key: str | Omit = omit, + prompt_cache_options: responses_client_event_param.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, safety_identifier: str | Omit = omit, @@ -4803,6 +4953,7 @@ async def create( "previous_response_id": previous_response_id, "prompt": prompt, "prompt_cache_key": prompt_cache_key, + "prompt_cache_options": prompt_cache_options, "prompt_cache_retention": prompt_cache_retention, "reasoning": reasoning, "safety_identifier": safety_identifier, diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md index 8e3c312eb0..8efcc3e33e 100644 --- a/src/openai/resources/webhooks/api.md +++ b/src/openai/resources/webhooks/api.md @@ -19,6 +19,7 @@ from openai.types.webhooks import ( ResponseCompletedWebhookEvent, ResponseFailedWebhookEvent, ResponseIncompleteWebhookEvent, + SafetyIdentifierBlockedWebhookEvent, UnwrapWebhookEvent, ) ``` diff --git a/src/openai/types/beta/__init__.py b/src/openai/types/beta/__init__.py index deb2369677..32e86f4601 100644 --- a/src/openai/types/beta/__init__.py +++ b/src/openai/types/beta/__init__.py @@ -4,31 +4,397 @@ from .thread import Thread as Thread from .assistant import Assistant as Assistant +from .beta_tool import BetaTool as BetaTool +from .beta_response import BetaResponse as BetaResponse from .function_tool import FunctionTool as FunctionTool from .assistant_tool import AssistantTool as AssistantTool from .thread_deleted import ThreadDeleted as ThreadDeleted +from .beta_tool_param import BetaToolParam as BetaToolParam +from .beta_custom_tool import BetaCustomTool as BetaCustomTool +from .beta_local_skill import BetaLocalSkill as BetaLocalSkill from .chatkit_workflow import ChatKitWorkflow as ChatKitWorkflow from .file_search_tool import FileSearchTool as FileSearchTool from .assistant_deleted import AssistantDeleted as AssistantDeleted +from .beta_inline_skill import BetaInlineSkill as BetaInlineSkill +from .beta_computer_tool import BetaComputerTool as BetaComputerTool +from .beta_function_tool import BetaFunctionTool as BetaFunctionTool +from .beta_response_item import BetaResponseItem as BetaResponseItem +from .beta_container_auto import BetaContainerAuto as BetaContainerAuto +from .beta_namespace_tool import BetaNamespaceTool as BetaNamespaceTool +from .beta_response_error import BetaResponseError as BetaResponseError +from .beta_response_input import BetaResponseInput as BetaResponseInput +from .beta_response_usage import BetaResponseUsage as BetaResponseUsage from .function_tool_param import FunctionToolParam as FunctionToolParam from .assistant_tool_param import AssistantToolParam as AssistantToolParam +from .beta_computer_action import BetaComputerAction as BetaComputerAction +from .beta_response_prompt import BetaResponsePrompt as BetaResponsePrompt +from .beta_response_status import BetaResponseStatus as BetaResponseStatus +from .beta_skill_reference import BetaSkillReference as BetaSkillReference +from .beta_tool_choice_mcp import BetaToolChoiceMcp as BetaToolChoiceMcp +from .beta_web_search_tool import BetaWebSearchTool as BetaWebSearchTool from .thread_create_params import ThreadCreateParams as ThreadCreateParams from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams from .assistant_list_params import AssistantListParams as AssistantListParams from .assistant_tool_choice import AssistantToolChoice as AssistantToolChoice +from .beta_apply_patch_tool import BetaApplyPatchTool as BetaApplyPatchTool +from .beta_file_search_tool import BetaFileSearchTool as BetaFileSearchTool +from .beta_tool_search_tool import BetaToolSearchTool as BetaToolSearchTool from .code_interpreter_tool import CodeInterpreterTool as CodeInterpreterTool from .assistant_stream_event import AssistantStreamEvent as AssistantStreamEvent +from .beta_custom_tool_param import BetaCustomToolParam as BetaCustomToolParam +from .beta_local_environment import BetaLocalEnvironment as BetaLocalEnvironment +from .beta_local_skill_param import BetaLocalSkillParam as BetaLocalSkillParam +from .beta_tool_choice_shell import BetaToolChoiceShell as BetaToolChoiceShell +from .beta_tool_choice_types import BetaToolChoiceTypes as BetaToolChoiceTypes from .file_search_tool_param import FileSearchToolParam as FileSearchToolParam +from .response_create_params import ResponseCreateParams as ResponseCreateParams from .assistant_create_params import AssistantCreateParams as AssistantCreateParams from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams +from .beta_compacted_response import BetaCompactedResponse as BetaCompactedResponse +from .beta_easy_input_message import BetaEasyInputMessage as BetaEasyInputMessage +from .beta_inline_skill_param import BetaInlineSkillParam as BetaInlineSkillParam +from .beta_tool_choice_custom import BetaToolChoiceCustom as BetaToolChoiceCustom +from .response_compact_params import ResponseCompactParams as ResponseCompactParams +from .beta_computer_tool_param import BetaComputerToolParam as BetaComputerToolParam +from .beta_container_reference import BetaContainerReference as BetaContainerReference +from .beta_function_shell_tool import BetaFunctionShellTool as BetaFunctionShellTool +from .beta_function_tool_param import BetaFunctionToolParam as BetaFunctionToolParam +from .beta_inline_skill_source import BetaInlineSkillSource as BetaInlineSkillSource +from .beta_response_includable import BetaResponseIncludable as BetaResponseIncludable +from .beta_response_input_file import BetaResponseInputFile as BetaResponseInputFile +from .beta_response_input_item import BetaResponseInputItem as BetaResponseInputItem +from .beta_response_input_text import BetaResponseInputText as BetaResponseInputText +from .beta_tool_choice_allowed import BetaToolChoiceAllowed as BetaToolChoiceAllowed +from .beta_tool_choice_options import BetaToolChoiceOptions as BetaToolChoiceOptions +from .response_retrieve_params import ResponseRetrieveParams as ResponseRetrieveParams +from .beta_computer_action_list import BetaComputerActionList as BetaComputerActionList +from .beta_container_auto_param import BetaContainerAutoParam as BetaContainerAutoParam +from .beta_namespace_tool_param import BetaNamespaceToolParam as BetaNamespaceToolParam +from .beta_response_error_event import BetaResponseErrorEvent as BetaResponseErrorEvent +from .beta_response_input_image import BetaResponseInputImage as BetaResponseInputImage +from .beta_response_input_param import BetaResponseInputParam as BetaResponseInputParam +from .beta_response_output_item import BetaResponseOutputItem as BetaResponseOutputItem +from .beta_response_output_text import BetaResponseOutputText as BetaResponseOutputText +from .beta_response_text_config import BetaResponseTextConfig as BetaResponseTextConfig +from .beta_tool_choice_function import BetaToolChoiceFunction as BetaToolChoiceFunction +from .beta_computer_action_param import BetaComputerActionParam as BetaComputerActionParam +from .beta_response_failed_event import BetaResponseFailedEvent as BetaResponseFailedEvent +from .beta_response_inject_event import BetaResponseInjectEvent as BetaResponseInjectEvent +from .beta_response_prompt_param import BetaResponsePromptParam as BetaResponsePromptParam +from .beta_response_queued_event import BetaResponseQueuedEvent as BetaResponseQueuedEvent +from .beta_response_stream_event import BetaResponseStreamEvent as BetaResponseStreamEvent +from .beta_skill_reference_param import BetaSkillReferenceParam as BetaSkillReferenceParam +from .beta_tool_choice_mcp_param import BetaToolChoiceMcpParam as BetaToolChoiceMcpParam +from .beta_web_search_tool_param import BetaWebSearchToolParam as BetaWebSearchToolParam from .assistant_tool_choice_param import AssistantToolChoiceParam as AssistantToolChoiceParam +from .beta_apply_patch_tool_param import BetaApplyPatchToolParam as BetaApplyPatchToolParam +from .beta_file_search_tool_param import BetaFileSearchToolParam as BetaFileSearchToolParam +from .beta_response_created_event import BetaResponseCreatedEvent as BetaResponseCreatedEvent +from .beta_response_input_content import BetaResponseInputContent as BetaResponseInputContent +from .beta_responses_client_event import BetaResponsesClientEvent as BetaResponsesClientEvent +from .beta_responses_server_event import BetaResponsesServerEvent as BetaResponsesServerEvent +from .beta_tool_search_tool_param import BetaToolSearchToolParam as BetaToolSearchToolParam from .code_interpreter_tool_param import CodeInterpreterToolParam as CodeInterpreterToolParam from .assistant_tool_choice_option import AssistantToolChoiceOption as AssistantToolChoiceOption +from .beta_local_environment_param import BetaLocalEnvironmentParam as BetaLocalEnvironmentParam +from .beta_response_output_message import BetaResponseOutputMessage as BetaResponseOutputMessage +from .beta_response_output_refusal import BetaResponseOutputRefusal as BetaResponseOutputRefusal +from .beta_response_reasoning_item import BetaResponseReasoningItem as BetaResponseReasoningItem +from .beta_tool_choice_apply_patch import BetaToolChoiceApplyPatch as BetaToolChoiceApplyPatch +from .beta_tool_choice_shell_param import BetaToolChoiceShellParam as BetaToolChoiceShellParam +from .beta_tool_choice_types_param import BetaToolChoiceTypesParam as BetaToolChoiceTypesParam +from .beta_web_search_preview_tool import BetaWebSearchPreviewTool as BetaWebSearchPreviewTool from .thread_create_and_run_params import ThreadCreateAndRunParams as ThreadCreateAndRunParams +from .beta_easy_input_message_param import BetaEasyInputMessageParam as BetaEasyInputMessageParam +from .beta_response_compaction_item import BetaResponseCompactionItem as BetaResponseCompactionItem +from .beta_response_completed_event import BetaResponseCompletedEvent as BetaResponseCompletedEvent +from .beta_response_text_done_event import BetaResponseTextDoneEvent as BetaResponseTextDoneEvent +from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam as BetaToolChoiceCustomParam from .assistant_tool_choice_function import AssistantToolChoiceFunction as AssistantToolChoiceFunction +from .beta_computer_use_preview_tool import BetaComputerUsePreviewTool as BetaComputerUsePreviewTool +from .beta_container_reference_param import BetaContainerReferenceParam as BetaContainerReferenceParam +from .beta_function_shell_tool_param import BetaFunctionShellToolParam as BetaFunctionShellToolParam +from .beta_inline_skill_source_param import BetaInlineSkillSourceParam as BetaInlineSkillSourceParam +from .beta_response_audio_done_event import BetaResponseAudioDoneEvent as BetaResponseAudioDoneEvent +from .beta_response_custom_tool_call import BetaResponseCustomToolCall as BetaResponseCustomToolCall +from .beta_response_incomplete_event import BetaResponseIncompleteEvent as BetaResponseIncompleteEvent +from .beta_response_input_file_param import BetaResponseInputFileParam as BetaResponseInputFileParam +from .beta_response_input_item_param import BetaResponseInputItemParam as BetaResponseInputItemParam +from .beta_response_input_text_param import BetaResponseInputTextParam as BetaResponseInputTextParam +from .beta_response_text_delta_event import BetaResponseTextDeltaEvent as BetaResponseTextDeltaEvent +from .beta_response_tool_search_call import BetaResponseToolSearchCall as BetaResponseToolSearchCall +from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam as BetaToolChoiceAllowedParam +from .beta_computer_action_list_param import BetaComputerActionListParam as BetaComputerActionListParam +from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent as BetaResponseAudioDeltaEvent +from .beta_response_in_progress_event import BetaResponseInProgressEvent as BetaResponseInProgressEvent +from .beta_response_input_image_param import BetaResponseInputImageParam as BetaResponseInputImageParam +from .beta_response_local_environment import BetaResponseLocalEnvironment as BetaResponseLocalEnvironment +from .beta_response_output_text_param import BetaResponseOutputTextParam as BetaResponseOutputTextParam +from .beta_response_text_config_param import BetaResponseTextConfigParam as BetaResponseTextConfigParam +from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam as BetaToolChoiceFunctionParam from .assistant_response_format_option import AssistantResponseFormatOption as AssistantResponseFormatOption +from .beta_response_computer_tool_call import BetaResponseComputerToolCall as BetaResponseComputerToolCall +from .beta_response_conversation_param import BetaResponseConversationParam as BetaResponseConversationParam +from .beta_response_format_text_config import BetaResponseFormatTextConfig as BetaResponseFormatTextConfig +from .beta_response_function_tool_call import BetaResponseFunctionToolCall as BetaResponseFunctionToolCall +from .beta_response_inject_event_param import BetaResponseInjectEventParam as BetaResponseInjectEventParam +from .beta_response_input_file_content import BetaResponseInputFileContent as BetaResponseInputFileContent +from .beta_response_input_message_item import BetaResponseInputMessageItem as BetaResponseInputMessageItem +from .beta_response_input_text_content import BetaResponseInputTextContent as BetaResponseInputTextContent +from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent as BetaResponseRefusalDoneEvent +from .beta_response_container_reference import BetaResponseContainerReference as BetaResponseContainerReference +from .beta_response_function_web_search import BetaResponseFunctionWebSearch as BetaResponseFunctionWebSearch +from .beta_response_inject_failed_event import BetaResponseInjectFailedEvent as BetaResponseInjectFailedEvent +from .beta_response_input_content_param import BetaResponseInputContentParam as BetaResponseInputContentParam +from .beta_response_input_image_content import BetaResponseInputImageContent as BetaResponseInputImageContent +from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent as BetaResponseRefusalDeltaEvent +from .beta_responses_client_event_param import BetaResponsesClientEventParam as BetaResponsesClientEventParam from .assistant_tool_choice_option_param import AssistantToolChoiceOptionParam as AssistantToolChoiceOptionParam +from .beta_response_inject_created_event import BetaResponseInjectCreatedEvent as BetaResponseInjectCreatedEvent +from .beta_response_output_message_param import BetaResponseOutputMessageParam as BetaResponseOutputMessageParam +from .beta_response_output_refusal_param import BetaResponseOutputRefusalParam as BetaResponseOutputRefusalParam +from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam as BetaResponseReasoningItemParam +from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam as BetaToolChoiceApplyPatchParam +from .beta_web_search_preview_tool_param import BetaWebSearchPreviewToolParam as BetaWebSearchPreviewToolParam +from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall as BetaResponseApplyPatchToolCall +from .beta_response_compaction_item_param import BetaResponseCompactionItemParam as BetaResponseCompactionItemParam +from .beta_response_custom_tool_call_item import BetaResponseCustomToolCallItem as BetaResponseCustomToolCallItem +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall as BetaResponseFileSearchToolCall +from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent as BetaResponseMcpCallFailedEvent from .assistant_tool_choice_function_param import AssistantToolChoiceFunctionParam as AssistantToolChoiceFunctionParam +from .beta_computer_use_preview_tool_param import BetaComputerUsePreviewToolParam as BetaComputerUsePreviewToolParam +from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam as BetaResponseCustomToolCallParam +from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent as BetaResponseOutputItemDoneEvent +from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent as BetaResponseContentPartDoneEvent +from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput as BetaResponseCustomToolCallOutput +from .beta_response_function_tool_call_item import BetaResponseFunctionToolCallItem as BetaResponseFunctionToolCallItem +from .beta_response_output_item_added_event import BetaResponseOutputItemAddedEvent as BetaResponseOutputItemAddedEvent +from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem as BetaResponseToolSearchOutputItem from .assistant_response_format_option_param import ( AssistantResponseFormatOptionParam as AssistantResponseFormatOptionParam, ) +from .beta_container_network_policy_disabled import ( + BetaContainerNetworkPolicyDisabled as BetaContainerNetworkPolicyDisabled, +) +from .beta_response_computer_tool_call_param import ( + BetaResponseComputerToolCallParam as BetaResponseComputerToolCallParam, +) +from .beta_response_content_part_added_event import ( + BetaResponseContentPartAddedEvent as BetaResponseContentPartAddedEvent, +) +from .beta_response_conversation_param_param import ( + BetaResponseConversationParamParam as BetaResponseConversationParamParam, +) +from .beta_response_format_text_config_param import ( + BetaResponseFormatTextConfigParam as BetaResponseFormatTextConfigParam, +) +from .beta_response_function_shell_tool_call import ( + BetaResponseFunctionShellToolCall as BetaResponseFunctionShellToolCall, +) +from .beta_response_function_tool_call_param import ( + BetaResponseFunctionToolCallParam as BetaResponseFunctionToolCallParam, +) +from .beta_response_input_file_content_param import ( + BetaResponseInputFileContentParam as BetaResponseInputFileContentParam, +) +from .beta_response_input_text_content_param import ( + BetaResponseInputTextContentParam as BetaResponseInputTextContentParam, +) +from .beta_response_mcp_call_completed_event import ( + BetaResponseMcpCallCompletedEvent as BetaResponseMcpCallCompletedEvent, +) +from .beta_container_network_policy_allowlist import ( + BetaContainerNetworkPolicyAllowlist as BetaContainerNetworkPolicyAllowlist, +) +from .beta_response_function_call_output_item import ( + BetaResponseFunctionCallOutputItem as BetaResponseFunctionCallOutputItem, +) +from .beta_response_function_web_search_param import ( + BetaResponseFunctionWebSearchParam as BetaResponseFunctionWebSearchParam, +) +from .beta_response_input_image_content_param import ( + BetaResponseInputImageContentParam as BetaResponseInputImageContentParam, +) +from .beta_response_reasoning_text_done_event import ( + BetaResponseReasoningTextDoneEvent as BetaResponseReasoningTextDoneEvent, +) +from .beta_response_code_interpreter_tool_call import ( + BetaResponseCodeInterpreterToolCall as BetaResponseCodeInterpreterToolCall, +) +from .beta_response_input_message_content_list import ( + BetaResponseInputMessageContentList as BetaResponseInputMessageContentList, +) +from .beta_response_mcp_call_in_progress_event import ( + BetaResponseMcpCallInProgressEvent as BetaResponseMcpCallInProgressEvent, +) +from .beta_response_reasoning_text_delta_event import ( + BetaResponseReasoningTextDeltaEvent as BetaResponseReasoningTextDeltaEvent, +) +from .beta_response_audio_transcript_done_event import ( + BetaResponseAudioTranscriptDoneEvent as BetaResponseAudioTranscriptDoneEvent, +) +from .beta_response_compaction_item_param_param import ( + BetaResponseCompactionItemParamParam as BetaResponseCompactionItemParamParam, +) +from .beta_response_file_search_tool_call_param import ( + BetaResponseFileSearchToolCallParam as BetaResponseFileSearchToolCallParam, +) +from .beta_response_mcp_list_tools_failed_event import ( + BetaResponseMcpListToolsFailedEvent as BetaResponseMcpListToolsFailedEvent, +) +from .beta_response_apply_patch_tool_call_output import ( + BetaResponseApplyPatchToolCallOutput as BetaResponseApplyPatchToolCallOutput, +) +from .beta_response_audio_transcript_delta_event import ( + BetaResponseAudioTranscriptDeltaEvent as BetaResponseAudioTranscriptDeltaEvent, +) +from .beta_response_custom_tool_call_output_item import ( + BetaResponseCustomToolCallOutputItem as BetaResponseCustomToolCallOutputItem, +) +from .beta_container_network_policy_domain_secret import ( + BetaContainerNetworkPolicyDomainSecret as BetaContainerNetworkPolicyDomainSecret, +) +from .beta_response_custom_tool_call_output_param import ( + BetaResponseCustomToolCallOutputParam as BetaResponseCustomToolCallOutputParam, +) +from .beta_response_mcp_call_arguments_done_event import ( + BetaResponseMcpCallArgumentsDoneEvent as BetaResponseMcpCallArgumentsDoneEvent, +) +from .beta_response_tool_search_output_item_param import ( + BetaResponseToolSearchOutputItemParam as BetaResponseToolSearchOutputItemParam, +) +from .beta_container_network_policy_disabled_param import ( + BetaContainerNetworkPolicyDisabledParam as BetaContainerNetworkPolicyDisabledParam, +) +from .beta_response_computer_tool_call_output_item import ( + BetaResponseComputerToolCallOutputItem as BetaResponseComputerToolCallOutputItem, +) +from .beta_response_format_text_json_schema_config import ( + BetaResponseFormatTextJSONSchemaConfig as BetaResponseFormatTextJSONSchemaConfig, +) +from .beta_response_function_call_output_item_list import ( + BetaResponseFunctionCallOutputItemList as BetaResponseFunctionCallOutputItemList, +) +from .beta_response_function_tool_call_output_item import ( + BetaResponseFunctionToolCallOutputItem as BetaResponseFunctionToolCallOutputItem, +) +from .beta_response_image_gen_call_completed_event import ( + BetaResponseImageGenCallCompletedEvent as BetaResponseImageGenCallCompletedEvent, +) +from .beta_response_mcp_call_arguments_delta_event import ( + BetaResponseMcpCallArgumentsDeltaEvent as BetaResponseMcpCallArgumentsDeltaEvent, +) +from .beta_response_mcp_list_tools_completed_event import ( + BetaResponseMcpListToolsCompletedEvent as BetaResponseMcpListToolsCompletedEvent, +) +from .beta_container_network_policy_allowlist_param import ( + BetaContainerNetworkPolicyAllowlistParam as BetaContainerNetworkPolicyAllowlistParam, +) +from .beta_response_function_call_output_item_param import ( + BetaResponseFunctionCallOutputItemParam as BetaResponseFunctionCallOutputItemParam, +) +from .beta_response_function_shell_tool_call_output import ( + BetaResponseFunctionShellToolCallOutput as BetaResponseFunctionShellToolCallOutput, +) +from .beta_response_image_gen_call_generating_event import ( + BetaResponseImageGenCallGeneratingEvent as BetaResponseImageGenCallGeneratingEvent, +) +from .beta_response_web_search_call_completed_event import ( + BetaResponseWebSearchCallCompletedEvent as BetaResponseWebSearchCallCompletedEvent, +) +from .beta_response_web_search_call_searching_event import ( + BetaResponseWebSearchCallSearchingEvent as BetaResponseWebSearchCallSearchingEvent, +) +from .beta_response_code_interpreter_tool_call_param import ( + BetaResponseCodeInterpreterToolCallParam as BetaResponseCodeInterpreterToolCallParam, +) +from .beta_response_file_search_call_completed_event import ( + BetaResponseFileSearchCallCompletedEvent as BetaResponseFileSearchCallCompletedEvent, +) +from .beta_response_file_search_call_searching_event import ( + BetaResponseFileSearchCallSearchingEvent as BetaResponseFileSearchCallSearchingEvent, +) +from .beta_response_image_gen_call_in_progress_event import ( + BetaResponseImageGenCallInProgressEvent as BetaResponseImageGenCallInProgressEvent, +) +from .beta_response_input_message_content_list_param import ( + BetaResponseInputMessageContentListParam as BetaResponseInputMessageContentListParam, +) +from .beta_response_mcp_list_tools_in_progress_event import ( + BetaResponseMcpListToolsInProgressEvent as BetaResponseMcpListToolsInProgressEvent, +) +from .beta_response_custom_tool_call_input_done_event import ( + BetaResponseCustomToolCallInputDoneEvent as BetaResponseCustomToolCallInputDoneEvent, +) +from .beta_response_reasoning_summary_part_done_event import ( + BetaResponseReasoningSummaryPartDoneEvent as BetaResponseReasoningSummaryPartDoneEvent, +) +from .beta_response_reasoning_summary_text_done_event import ( + BetaResponseReasoningSummaryTextDoneEvent as BetaResponseReasoningSummaryTextDoneEvent, +) +from .beta_response_web_search_call_in_progress_event import ( + BetaResponseWebSearchCallInProgressEvent as BetaResponseWebSearchCallInProgressEvent, +) +from .beta_response_custom_tool_call_input_delta_event import ( + BetaResponseCustomToolCallInputDeltaEvent as BetaResponseCustomToolCallInputDeltaEvent, +) +from .beta_response_file_search_call_in_progress_event import ( + BetaResponseFileSearchCallInProgressEvent as BetaResponseFileSearchCallInProgressEvent, +) +from .beta_response_function_call_arguments_done_event import ( + BetaResponseFunctionCallArgumentsDoneEvent as BetaResponseFunctionCallArgumentsDoneEvent, +) +from .beta_response_function_shell_call_output_content import ( + BetaResponseFunctionShellCallOutputContent as BetaResponseFunctionShellCallOutputContent, +) +from .beta_response_image_gen_call_partial_image_event import ( + BetaResponseImageGenCallPartialImageEvent as BetaResponseImageGenCallPartialImageEvent, +) +from .beta_response_output_text_annotation_added_event import ( + BetaResponseOutputTextAnnotationAddedEvent as BetaResponseOutputTextAnnotationAddedEvent, +) +from .beta_response_reasoning_summary_part_added_event import ( + BetaResponseReasoningSummaryPartAddedEvent as BetaResponseReasoningSummaryPartAddedEvent, +) +from .beta_response_reasoning_summary_text_delta_event import ( + BetaResponseReasoningSummaryTextDeltaEvent as BetaResponseReasoningSummaryTextDeltaEvent, +) +from .beta_container_network_policy_domain_secret_param import ( + BetaContainerNetworkPolicyDomainSecretParam as BetaContainerNetworkPolicyDomainSecretParam, +) +from .beta_response_function_call_arguments_delta_event import ( + BetaResponseFunctionCallArgumentsDeltaEvent as BetaResponseFunctionCallArgumentsDeltaEvent, +) +from .beta_response_tool_search_output_item_param_param import ( + BetaResponseToolSearchOutputItemParamParam as BetaResponseToolSearchOutputItemParamParam, +) +from .beta_response_computer_tool_call_output_screenshot import ( + BetaResponseComputerToolCallOutputScreenshot as BetaResponseComputerToolCallOutputScreenshot, +) +from .beta_response_format_text_json_schema_config_param import ( + BetaResponseFormatTextJSONSchemaConfigParam as BetaResponseFormatTextJSONSchemaConfigParam, +) +from .beta_response_function_call_output_item_list_param import ( + BetaResponseFunctionCallOutputItemListParam as BetaResponseFunctionCallOutputItemListParam, +) +from .beta_response_code_interpreter_call_code_done_event import ( + BetaResponseCodeInterpreterCallCodeDoneEvent as BetaResponseCodeInterpreterCallCodeDoneEvent, +) +from .beta_response_code_interpreter_call_completed_event import ( + BetaResponseCodeInterpreterCallCompletedEvent as BetaResponseCodeInterpreterCallCompletedEvent, +) +from .beta_response_code_interpreter_call_code_delta_event import ( + BetaResponseCodeInterpreterCallCodeDeltaEvent as BetaResponseCodeInterpreterCallCodeDeltaEvent, +) +from .beta_response_code_interpreter_call_in_progress_event import ( + BetaResponseCodeInterpreterCallInProgressEvent as BetaResponseCodeInterpreterCallInProgressEvent, +) +from .beta_response_code_interpreter_call_interpreting_event import ( + BetaResponseCodeInterpreterCallInterpretingEvent as BetaResponseCodeInterpreterCallInterpretingEvent, +) +from .beta_response_function_shell_call_output_content_param import ( + BetaResponseFunctionShellCallOutputContentParam as BetaResponseFunctionShellCallOutputContentParam, +) +from .beta_response_computer_tool_call_output_screenshot_param import ( + BetaResponseComputerToolCallOutputScreenshotParam as BetaResponseComputerToolCallOutputScreenshotParam, +) diff --git a/src/openai/types/beta/assistant_create_params.py b/src/openai/types/beta/assistant_create_params.py index 5a468a351b..24e2122cce 100644 --- a/src/openai/types/beta/assistant_create_params.py +++ b/src/openai/types/beta/assistant_create_params.py @@ -59,20 +59,14 @@ class AssistantCreateParams(TypedDict, total=False): """The name of the assistant. The maximum length is 256 characters.""" reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/assistant_update_params.py b/src/openai/types/beta/assistant_update_params.py index 7896fcd9c6..e939338615 100644 --- a/src/openai/types/beta/assistant_update_params.py +++ b/src/openai/types/beta/assistant_update_params.py @@ -94,20 +94,14 @@ class AssistantUpdateParams(TypedDict, total=False): """The name of the assistant. The maximum length is 256 characters.""" reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/beta/beta_apply_patch_tool.py b/src/openai/types/beta/beta_apply_patch_tool.py new file mode 100644 index 0000000000..52e17f2a3d --- /dev/null +++ b/src/openai/types/beta/beta_apply_patch_tool.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaApplyPatchTool"] + + +class BetaApplyPatchTool(BaseModel): + """Allows the assistant to create, delete, or update files using unified diffs.""" + + type: Literal["apply_patch"] + """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" diff --git a/src/openai/types/beta/beta_apply_patch_tool_param.py b/src/openai/types/beta/beta_apply_patch_tool_param.py new file mode 100644 index 0000000000..ad3b857e47 --- /dev/null +++ b/src/openai/types/beta/beta_apply_patch_tool_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaApplyPatchToolParam"] + + +class BetaApplyPatchToolParam(TypedDict, total=False): + """Allows the assistant to create, delete, or update files using unified diffs.""" + + type: Required[Literal["apply_patch"]] + """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" diff --git a/src/openai/types/beta/beta_compacted_response.py b/src/openai/types/beta/beta_compacted_response.py new file mode 100644 index 0000000000..9d57670fde --- /dev/null +++ b/src/openai/types/beta/beta_compacted_response.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_usage import BetaResponseUsage +from .beta_response_output_item import BetaResponseOutputItem + +__all__ = ["BetaCompactedResponse"] + + +class BetaCompactedResponse(BaseModel): + id: str + """The unique identifier for the compacted response.""" + + created_at: int + """Unix timestamp (in seconds) when the compacted conversation was created.""" + + object: Literal["response.compaction"] + """The object type. Always `response.compaction`.""" + + output: List[BetaResponseOutputItem] + """The compacted list of output items. + + This is a list of all user messages, followed by a single compaction item. + """ + + usage: BetaResponseUsage + """ + Token accounting for the compaction pass, including cached, reasoning, and total + tokens. + """ diff --git a/src/openai/types/beta/beta_computer_action.py b/src/openai/types/beta/beta_computer_action.py new file mode 100644 index 0000000000..146ea7b6ae --- /dev/null +++ b/src/openai/types/beta/beta_computer_action.py @@ -0,0 +1,196 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaComputerAction", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(BaseModel): + """A click action.""" + + button: Literal["left", "right", "wheel", "back", "forward"] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Literal["click"] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: int + """The x-coordinate where the click occurred.""" + + y: int + """The y-coordinate where the click occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while clicking.""" + + +class DoubleClick(BaseModel): + """A double click action.""" + + keys: Optional[List[str]] = None + """The keys being held while double-clicking.""" + + type: Literal["double_click"] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: int + """The x-coordinate where the double click occurred.""" + + y: int + """The y-coordinate where the double click occurred.""" + + +class DragPath(BaseModel): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: int + """The x-coordinate.""" + + y: int + """The y-coordinate.""" + + +class Drag(BaseModel): + """A drag action.""" + + path: List[DragPath] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Literal["drag"] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[List[str]] = None + """The keys being held while dragging the mouse.""" + + +class Keypress(BaseModel): + """A collection of keypresses the model would like to perform.""" + + keys: List[str] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Literal["keypress"] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(BaseModel): + """A mouse move action.""" + + type: Literal["move"] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: int + """The x-coordinate to move to.""" + + y: int + """The y-coordinate to move to.""" + + keys: Optional[List[str]] = None + """The keys being held while moving the mouse.""" + + +class Screenshot(BaseModel): + """A screenshot action.""" + + type: Literal["screenshot"] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(BaseModel): + """A scroll action.""" + + scroll_x: int + """The horizontal scroll distance.""" + + scroll_y: int + """The vertical scroll distance.""" + + type: Literal["scroll"] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: int + """The x-coordinate where the scroll occurred.""" + + y: int + """The y-coordinate where the scroll occurred.""" + + keys: Optional[List[str]] = None + """The keys being held while scrolling.""" + + +class Type(BaseModel): + """An action to type in text.""" + + text: str + """The text to type.""" + + type: Literal["type"] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(BaseModel): + """A wait action.""" + + type: Literal["wait"] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +BetaComputerAction: TypeAlias = Annotated[ + Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/beta_computer_action_list.py b/src/openai/types/beta/beta_computer_action_list.py new file mode 100644 index 0000000000..a90eb35c16 --- /dev/null +++ b/src/openai/types/beta/beta_computer_action_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_computer_action import BetaComputerAction + +__all__ = ["BetaComputerActionList"] + +BetaComputerActionList: TypeAlias = List[BetaComputerAction] diff --git a/src/openai/types/beta/beta_computer_action_list_param.py b/src/openai/types/beta/beta_computer_action_list_param.py new file mode 100644 index 0000000000..7fe5e87b71 --- /dev/null +++ b/src/openai/types/beta/beta_computer_action_list_param.py @@ -0,0 +1,198 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaComputerActionListParam", + "BetaComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +BetaComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] + +BetaComputerActionListParam: TypeAlias = List[BetaComputerActionParam] diff --git a/src/openai/types/beta/beta_computer_action_param.py b/src/openai/types/beta/beta_computer_action_param.py new file mode 100644 index 0000000000..bd1ddbb3e7 --- /dev/null +++ b/src/openai/types/beta/beta_computer_action_param.py @@ -0,0 +1,195 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaComputerActionParam", + "Click", + "DoubleClick", + "Drag", + "DragPath", + "Keypress", + "Move", + "Screenshot", + "Scroll", + "Type", + "Wait", +] + + +class Click(TypedDict, total=False): + """A click action.""" + + button: Required[Literal["left", "right", "wheel", "back", "forward"]] + """Indicates which mouse button was pressed during the click. + + One of `left`, `right`, `wheel`, `back`, or `forward`. + """ + + type: Required[Literal["click"]] + """Specifies the event type. For a click action, this property is always `click`.""" + + x: Required[int] + """The x-coordinate where the click occurred.""" + + y: Required[int] + """The y-coordinate where the click occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while clicking.""" + + +class DoubleClick(TypedDict, total=False): + """A double click action.""" + + keys: Required[Optional[SequenceNotStr[str]]] + """The keys being held while double-clicking.""" + + type: Required[Literal["double_click"]] + """Specifies the event type. + + For a double click action, this property is always set to `double_click`. + """ + + x: Required[int] + """The x-coordinate where the double click occurred.""" + + y: Required[int] + """The y-coordinate where the double click occurred.""" + + +class DragPath(TypedDict, total=False): + """An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.""" + + x: Required[int] + """The x-coordinate.""" + + y: Required[int] + """The y-coordinate.""" + + +class Drag(TypedDict, total=False): + """A drag action.""" + + path: Required[Iterable[DragPath]] + """An array of coordinates representing the path of the drag action. + + Coordinates will appear as an array of objects, eg + + ``` + [ + { x: 100, y: 200 }, + { x: 200, y: 300 } + ] + ``` + """ + + type: Required[Literal["drag"]] + """Specifies the event type. + + For a drag action, this property is always set to `drag`. + """ + + keys: Optional[SequenceNotStr[str]] + """The keys being held while dragging the mouse.""" + + +class Keypress(TypedDict, total=False): + """A collection of keypresses the model would like to perform.""" + + keys: Required[SequenceNotStr[str]] + """The combination of keys the model is requesting to be pressed. + + This is an array of strings, each representing a key. + """ + + type: Required[Literal["keypress"]] + """Specifies the event type. + + For a keypress action, this property is always set to `keypress`. + """ + + +class Move(TypedDict, total=False): + """A mouse move action.""" + + type: Required[Literal["move"]] + """Specifies the event type. + + For a move action, this property is always set to `move`. + """ + + x: Required[int] + """The x-coordinate to move to.""" + + y: Required[int] + """The y-coordinate to move to.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while moving the mouse.""" + + +class Screenshot(TypedDict, total=False): + """A screenshot action.""" + + type: Required[Literal["screenshot"]] + """Specifies the event type. + + For a screenshot action, this property is always set to `screenshot`. + """ + + +class Scroll(TypedDict, total=False): + """A scroll action.""" + + scroll_x: Required[int] + """The horizontal scroll distance.""" + + scroll_y: Required[int] + """The vertical scroll distance.""" + + type: Required[Literal["scroll"]] + """Specifies the event type. + + For a scroll action, this property is always set to `scroll`. + """ + + x: Required[int] + """The x-coordinate where the scroll occurred.""" + + y: Required[int] + """The y-coordinate where the scroll occurred.""" + + keys: Optional[SequenceNotStr[str]] + """The keys being held while scrolling.""" + + +class Type(TypedDict, total=False): + """An action to type in text.""" + + text: Required[str] + """The text to type.""" + + type: Required[Literal["type"]] + """Specifies the event type. + + For a type action, this property is always set to `type`. + """ + + +class Wait(TypedDict, total=False): + """A wait action.""" + + type: Required[Literal["wait"]] + """Specifies the event type. + + For a wait action, this property is always set to `wait`. + """ + + +BetaComputerActionParam: TypeAlias = Union[Click, DoubleClick, Drag, Keypress, Move, Screenshot, Scroll, Type, Wait] diff --git a/src/openai/types/beta/beta_computer_tool.py b/src/openai/types/beta/beta_computer_tool.py new file mode 100644 index 0000000000..84d92748fd --- /dev/null +++ b/src/openai/types/beta/beta_computer_tool.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaComputerTool"] + + +class BetaComputerTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + type: Literal["computer"] + """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/beta/beta_computer_tool_param.py b/src/openai/types/beta/beta_computer_tool_param.py new file mode 100644 index 0000000000..eb17456b80 --- /dev/null +++ b/src/openai/types/beta/beta_computer_tool_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaComputerToolParam"] + + +class BetaComputerToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + type: Required[Literal["computer"]] + """The type of the computer tool. Always `computer`.""" diff --git a/src/openai/types/beta/beta_computer_use_preview_tool.py b/src/openai/types/beta/beta_computer_use_preview_tool.py new file mode 100644 index 0000000000..cc7a11a4ce --- /dev/null +++ b/src/openai/types/beta/beta_computer_use_preview_tool.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaComputerUsePreviewTool"] + + +class BetaComputerUsePreviewTool(BaseModel): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + display_height: int + """The height of the computer display.""" + + display_width: int + """The width of the computer display.""" + + environment: Literal["windows", "mac", "linux", "ubuntu", "browser"] + """The type of computer environment to control.""" + + type: Literal["computer_use_preview"] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/src/openai/types/beta/beta_computer_use_preview_tool_param.py b/src/openai/types/beta/beta_computer_use_preview_tool_param.py new file mode 100644 index 0000000000..26e52fd42a --- /dev/null +++ b/src/openai/types/beta/beta_computer_use_preview_tool_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaComputerUsePreviewToolParam"] + + +class BetaComputerUsePreviewToolParam(TypedDict, total=False): + """A tool that controls a virtual computer. + + Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use). + """ + + display_height: Required[int] + """The height of the computer display.""" + + display_width: Required[int] + """The width of the computer display.""" + + environment: Required[Literal["windows", "mac", "linux", "ubuntu", "browser"]] + """The type of computer environment to control.""" + + type: Required[Literal["computer_use_preview"]] + """The type of the computer use tool. Always `computer_use_preview`.""" diff --git a/src/openai/types/beta/beta_container_auto.py b/src/openai/types/beta/beta_container_auto.py new file mode 100644 index 0000000000..2b59366773 --- /dev/null +++ b/src/openai/types/beta/beta_container_auto.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_inline_skill import BetaInlineSkill +from .beta_skill_reference import BetaSkillReference +from .beta_container_network_policy_disabled import BetaContainerNetworkPolicyDisabled +from .beta_container_network_policy_allowlist import BetaContainerNetworkPolicyAllowlist + +__all__ = ["BetaContainerAuto", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Annotated[ + Union[BetaContainerNetworkPolicyDisabled, BetaContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + +Skill: TypeAlias = Annotated[Union[BetaSkillReference, BetaInlineSkill], PropertyInfo(discriminator="type")] + + +class BetaContainerAuto(BaseModel): + type: Literal["container_auto"] + """Automatically creates a container for this request""" + + file_ids: Optional[List[str]] = None + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the container.""" + + network_policy: Optional[NetworkPolicy] = None + """Network access policy for the container.""" + + skills: Optional[List[Skill]] = None + """An optional list of skills referenced by id or inline data.""" diff --git a/src/openai/types/beta/beta_container_auto_param.py b/src/openai/types/beta/beta_container_auto_param.py new file mode 100644 index 0000000000..532c3d3a32 --- /dev/null +++ b/src/openai/types/beta/beta_container_auto_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_inline_skill_param import BetaInlineSkillParam +from .beta_skill_reference_param import BetaSkillReferenceParam +from .beta_container_network_policy_disabled_param import BetaContainerNetworkPolicyDisabledParam +from .beta_container_network_policy_allowlist_param import BetaContainerNetworkPolicyAllowlistParam + +__all__ = ["BetaContainerAutoParam", "NetworkPolicy", "Skill"] + +NetworkPolicy: TypeAlias = Union[BetaContainerNetworkPolicyDisabledParam, BetaContainerNetworkPolicyAllowlistParam] + +Skill: TypeAlias = Union[BetaSkillReferenceParam, BetaInlineSkillParam] + + +class BetaContainerAutoParam(TypedDict, total=False): + type: Required[Literal["container_auto"]] + """Automatically creates a container for this request""" + + file_ids: SequenceNotStr[str] + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the container.""" + + network_policy: NetworkPolicy + """Network access policy for the container.""" + + skills: Iterable[Skill] + """An optional list of skills referenced by id or inline data.""" diff --git a/src/openai/types/beta/beta_container_network_policy_allowlist.py b/src/openai/types/beta/beta_container_network_policy_allowlist.py new file mode 100644 index 0000000000..de1aec6476 --- /dev/null +++ b/src/openai/types/beta/beta_container_network_policy_allowlist.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_container_network_policy_domain_secret import BetaContainerNetworkPolicyDomainSecret + +__all__ = ["BetaContainerNetworkPolicyAllowlist"] + + +class BetaContainerNetworkPolicyAllowlist(BaseModel): + allowed_domains: List[str] + """A list of allowed domains when type is `allowlist`.""" + + type: Literal["allowlist"] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Optional[List[BetaContainerNetworkPolicyDomainSecret]] = None + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/src/openai/types/beta/beta_container_network_policy_allowlist_param.py b/src/openai/types/beta/beta_container_network_policy_allowlist_param.py new file mode 100644 index 0000000000..49c7f626c0 --- /dev/null +++ b/src/openai/types/beta/beta_container_network_policy_allowlist_param.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr +from .beta_container_network_policy_domain_secret_param import BetaContainerNetworkPolicyDomainSecretParam + +__all__ = ["BetaContainerNetworkPolicyAllowlistParam"] + + +class BetaContainerNetworkPolicyAllowlistParam(TypedDict, total=False): + allowed_domains: Required[SequenceNotStr[str]] + """A list of allowed domains when type is `allowlist`.""" + + type: Required[Literal["allowlist"]] + """Allow outbound network access only to specified domains. Always `allowlist`.""" + + domain_secrets: Iterable[BetaContainerNetworkPolicyDomainSecretParam] + """Optional domain-scoped secrets for allowlisted domains.""" diff --git a/src/openai/types/beta/beta_container_network_policy_disabled.py b/src/openai/types/beta/beta_container_network_policy_disabled.py new file mode 100644 index 0000000000..8bf050fce4 --- /dev/null +++ b/src/openai/types/beta/beta_container_network_policy_disabled.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaContainerNetworkPolicyDisabled"] + + +class BetaContainerNetworkPolicyDisabled(BaseModel): + type: Literal["disabled"] + """Disable outbound network access. Always `disabled`.""" diff --git a/src/openai/types/beta/beta_container_network_policy_disabled_param.py b/src/openai/types/beta/beta_container_network_policy_disabled_param.py new file mode 100644 index 0000000000..6310702925 --- /dev/null +++ b/src/openai/types/beta/beta_container_network_policy_disabled_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaContainerNetworkPolicyDisabledParam"] + + +class BetaContainerNetworkPolicyDisabledParam(TypedDict, total=False): + type: Required[Literal["disabled"]] + """Disable outbound network access. Always `disabled`.""" diff --git a/src/openai/types/beta/beta_container_network_policy_domain_secret.py b/src/openai/types/beta/beta_container_network_policy_domain_secret.py new file mode 100644 index 0000000000..68109efcb9 --- /dev/null +++ b/src/openai/types/beta/beta_container_network_policy_domain_secret.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaContainerNetworkPolicyDomainSecret"] + + +class BetaContainerNetworkPolicyDomainSecret(BaseModel): + domain: str + """The domain associated with the secret.""" + + name: str + """The name of the secret to inject for the domain.""" + + value: str + """The secret value to inject for the domain.""" diff --git a/src/openai/types/beta/beta_container_network_policy_domain_secret_param.py b/src/openai/types/beta/beta_container_network_policy_domain_secret_param.py new file mode 100644 index 0000000000..28fb2954a0 --- /dev/null +++ b/src/openai/types/beta/beta_container_network_policy_domain_secret_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BetaContainerNetworkPolicyDomainSecretParam"] + + +class BetaContainerNetworkPolicyDomainSecretParam(TypedDict, total=False): + domain: Required[str] + """The domain associated with the secret.""" + + name: Required[str] + """The name of the secret to inject for the domain.""" + + value: Required[str] + """The secret value to inject for the domain.""" diff --git a/src/openai/types/beta/beta_container_reference.py b/src/openai/types/beta/beta_container_reference.py new file mode 100644 index 0000000000..b187918b87 --- /dev/null +++ b/src/openai/types/beta/beta_container_reference.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaContainerReference"] + + +class BetaContainerReference(BaseModel): + container_id: str + """The ID of the referenced container.""" + + type: Literal["container_reference"] + """References a container created with the /v1/containers endpoint""" diff --git a/src/openai/types/beta/beta_container_reference_param.py b/src/openai/types/beta/beta_container_reference_param.py new file mode 100644 index 0000000000..2a21da41c6 --- /dev/null +++ b/src/openai/types/beta/beta_container_reference_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaContainerReferenceParam"] + + +class BetaContainerReferenceParam(TypedDict, total=False): + container_id: Required[str] + """The ID of the referenced container.""" + + type: Required[Literal["container_reference"]] + """References a container created with the /v1/containers endpoint""" diff --git a/src/openai/types/beta/beta_custom_tool.py b/src/openai/types/beta/beta_custom_tool.py new file mode 100644 index 0000000000..a7574a5e8e --- /dev/null +++ b/src/openai/types/beta/beta_custom_tool.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaCustomTool", "Format", "FormatText", "FormatGrammar"] + + +class FormatText(BaseModel): + """Unconstrained free-form text.""" + + type: Literal["text"] + """Unconstrained text format. Always `text`.""" + + +class FormatGrammar(BaseModel): + """A grammar defined by the user.""" + + definition: str + """The grammar definition.""" + + syntax: Literal["lark", "regex"] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Literal["grammar"] + """Grammar format. Always `grammar`.""" + + +Format: TypeAlias = Annotated[Union[FormatText, FormatGrammar], PropertyInfo(discriminator="type")] + + +class BetaCustomTool(BaseModel): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + """ + + name: str + """The name of the custom tool, used to identify it in tool calls.""" + + type: Literal["custom"] + """The type of the custom tool. Always `custom`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + defer_loading: Optional[bool] = None + """Whether this tool should be deferred and discovered via tool search.""" + + description: Optional[str] = None + """Optional description of the custom tool, used to provide more context.""" + + format: Optional[Format] = None + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/src/openai/types/beta/beta_custom_tool_param.py b/src/openai/types/beta/beta_custom_tool_param.py new file mode 100644 index 0000000000..eb3c7dcfca --- /dev/null +++ b/src/openai/types/beta/beta_custom_tool_param.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaCustomToolParam", "Format", "FormatText", "FormatGrammar"] + + +class FormatText(TypedDict, total=False): + """Unconstrained free-form text.""" + + type: Required[Literal["text"]] + """Unconstrained text format. Always `text`.""" + + +class FormatGrammar(TypedDict, total=False): + """A grammar defined by the user.""" + + definition: Required[str] + """The grammar definition.""" + + syntax: Required[Literal["lark", "regex"]] + """The syntax of the grammar definition. One of `lark` or `regex`.""" + + type: Required[Literal["grammar"]] + """Grammar format. Always `grammar`.""" + + +Format: TypeAlias = Union[FormatText, FormatGrammar] + + +class BetaCustomToolParam(TypedDict, total=False): + """A custom tool that processes input using a specified format. + + Learn more about [custom tools](https://platform.openai.com/docs/guides/function-calling#custom-tools) + """ + + name: Required[str] + """The name of the custom tool, used to identify it in tool calls.""" + + type: Required[Literal["custom"]] + """The type of the custom tool. Always `custom`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this tool should be deferred and discovered via tool search.""" + + description: str + """Optional description of the custom tool, used to provide more context.""" + + format: Format + """The input format for the custom tool. Default is unconstrained text.""" diff --git a/src/openai/types/beta/beta_easy_input_message.py b/src/openai/types/beta/beta_easy_input_message.py new file mode 100644 index 0000000000..256cfc2188 --- /dev/null +++ b/src/openai/types/beta/beta_easy_input_message.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_message_content_list import BetaResponseInputMessageContentList + +__all__ = ["BetaEasyInputMessage"] + + +class BetaEasyInputMessage(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Union[str, BetaResponseInputMessageContentList] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Literal["user", "assistant", "system", "developer"] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always `message`.""" diff --git a/src/openai/types/beta/beta_easy_input_message_param.py b/src/openai/types/beta/beta_easy_input_message_param.py new file mode 100644 index 0000000000..317119c7c2 --- /dev/null +++ b/src/openai/types/beta/beta_easy_input_message_param.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam + +__all__ = ["BetaEasyInputMessageParam"] + + +class BetaEasyInputMessageParam(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. Messages with the + `assistant` role are presumed to have been generated by the model in previous + interactions. + """ + + content: Required[Union[str, BetaResponseInputMessageContentListParam]] + """ + Text, image, or audio input to the model, used to generate a response. Can also + contain previous assistant responses. + """ + + role: Required[Literal["user", "assistant", "system", "developer"]] + """The role of the message input. + + One of `user`, `assistant`, `system`, or `developer`. + """ + + phase: Optional[Literal["commentary", "final_answer"]] + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ + + type: Literal["message"] + """The type of the message input. Always `message`.""" diff --git a/src/openai/types/beta/beta_file_search_tool.py b/src/openai/types/beta/beta_file_search_tool.py new file mode 100644 index 0000000000..509d2c4d96 --- /dev/null +++ b/src/openai/types/beta/beta_file_search_tool.py @@ -0,0 +1,153 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel + +__all__ = [ + "BetaFileSearchTool", + "Filters", + "FiltersComparisonFilter", + "FiltersCompoundFilter", + "FiltersCompoundFilterFilter", + "FiltersCompoundFilterFilterComparisonFilter", + "RankingOptions", + "RankingOptionsHybridSearch", +] + + +class FiltersComparisonFilter(BaseModel): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: str + """The key to compare against the value.""" + + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Union[str, float, bool, List[Union[str, float]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +class FiltersCompoundFilterFilterComparisonFilter(BaseModel): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: str + """The key to compare against the value.""" + + type: Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Union[str, float, bool, List[Union[str, float]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +FiltersCompoundFilterFilter: TypeAlias = Union[FiltersCompoundFilterFilterComparisonFilter, object] + + +class FiltersCompoundFilter(BaseModel): + """Combine multiple filters using `and` or `or`.""" + + filters: List[FiltersCompoundFilterFilter] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Literal["and", "or"] + """Type of operation: `and` or `or`.""" + + +Filters: TypeAlias = Union[FiltersComparisonFilter, FiltersCompoundFilter, None] + + +class RankingOptionsHybridSearch(BaseModel): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + + embedding_weight: float + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: float + """The weight of the text in the reciprocal ranking fusion.""" + + +class RankingOptions(BaseModel): + """Ranking options for search.""" + + hybrid_search: Optional[RankingOptionsHybridSearch] = None + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + + ranker: Optional[Literal["auto", "default-2024-11-15"]] = None + """The ranker to use for the file search.""" + + score_threshold: Optional[float] = None + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. + """ + + +class BetaFileSearchTool(BaseModel): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search). + """ + + type: Literal["file_search"] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: List[str] + """The IDs of the vector stores to search.""" + + filters: Optional[Filters] = None + """A filter to apply.""" + + max_num_results: Optional[int] = None + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: Optional[RankingOptions] = None + """Ranking options for search.""" diff --git a/src/openai/types/beta/beta_file_search_tool_param.py b/src/openai/types/beta/beta_file_search_tool_param.py new file mode 100644 index 0000000000..089f3721da --- /dev/null +++ b/src/openai/types/beta/beta_file_search_tool_param.py @@ -0,0 +1,155 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaFileSearchToolParam", + "Filters", + "FiltersComparisonFilter", + "FiltersCompoundFilter", + "FiltersCompoundFilterFilter", + "FiltersCompoundFilterFilterComparisonFilter", + "RankingOptions", + "RankingOptionsHybridSearch", +] + + +class FiltersComparisonFilter(TypedDict, total=False): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: Required[str] + """The key to compare against the value.""" + + type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Required[Union[str, float, bool, SequenceNotStr[Union[str, float]]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +class FiltersCompoundFilterFilterComparisonFilter(TypedDict, total=False): + """ + A filter used to compare a specified attribute key to a given value using a defined comparison operation. + """ + + key: Required[str] + """The key to compare against the value.""" + + type: Required[Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin"]] + """ + Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, + `nin`. + + - `eq`: equals + - `ne`: not equal + - `gt`: greater than + - `gte`: greater than or equal + - `lt`: less than + - `lte`: less than or equal + - `in`: in + - `nin`: not in + """ + + value: Required[Union[str, float, bool, SequenceNotStr[Union[str, float]]]] + """ + The value to compare against the attribute key; supports string, number, or + boolean types. + """ + + +FiltersCompoundFilterFilter: TypeAlias = Union[FiltersCompoundFilterFilterComparisonFilter, object] + + +class FiltersCompoundFilter(TypedDict, total=False): + """Combine multiple filters using `and` or `or`.""" + + filters: Required[Iterable[FiltersCompoundFilterFilter]] + """Array of filters to combine. + + Items can be `ComparisonFilter` or `CompoundFilter`. + """ + + type: Required[Literal["and", "or"]] + """Type of operation: `and` or `or`.""" + + +Filters: TypeAlias = Union[FiltersComparisonFilter, FiltersCompoundFilter] + + +class RankingOptionsHybridSearch(TypedDict, total=False): + """ + Weights that control how reciprocal rank fusion balances semantic embedding matches versus sparse keyword matches when hybrid search is enabled. + """ + + embedding_weight: Required[float] + """The weight of the embedding in the reciprocal ranking fusion.""" + + text_weight: Required[float] + """The weight of the text in the reciprocal ranking fusion.""" + + +class RankingOptions(TypedDict, total=False): + """Ranking options for search.""" + + hybrid_search: RankingOptionsHybridSearch + """ + Weights that control how reciprocal rank fusion balances semantic embedding + matches versus sparse keyword matches when hybrid search is enabled. + """ + + ranker: Literal["auto", "default-2024-11-15"] + """The ranker to use for the file search.""" + + score_threshold: float + """The score threshold for the file search, a number between 0 and 1. + + Numbers closer to 1 will attempt to return only the most relevant results, but + may return fewer results. + """ + + +class BetaFileSearchToolParam(TypedDict, total=False): + """A tool that searches for relevant content from uploaded files. + + Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search). + """ + + type: Required[Literal["file_search"]] + """The type of the file search tool. Always `file_search`.""" + + vector_store_ids: Required[SequenceNotStr[str]] + """The IDs of the vector stores to search.""" + + filters: Optional[Filters] + """A filter to apply.""" + + max_num_results: int + """The maximum number of results to return. + + This number should be between 1 and 50 inclusive. + """ + + ranking_options: RankingOptions + """Ranking options for search.""" diff --git a/src/openai/types/beta/beta_function_shell_tool.py b/src/openai/types/beta/beta_function_shell_tool.py new file mode 100644 index 0000000000..dc3849a656 --- /dev/null +++ b/src/openai/types/beta/beta_function_shell_tool.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_container_auto import BetaContainerAuto +from .beta_local_environment import BetaLocalEnvironment +from .beta_container_reference import BetaContainerReference + +__all__ = ["BetaFunctionShellTool", "Environment"] + +Environment: TypeAlias = Annotated[ + Union[BetaContainerAuto, BetaLocalEnvironment, BetaContainerReference, None], PropertyInfo(discriminator="type") +] + + +class BetaFunctionShellTool(BaseModel): + """A tool that allows the model to execute shell commands.""" + + type: Literal["shell"] + """The type of the shell tool. Always `shell`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + environment: Optional[Environment] = None diff --git a/src/openai/types/beta/beta_function_shell_tool_param.py b/src/openai/types/beta/beta_function_shell_tool_param.py new file mode 100644 index 0000000000..985b68a6ec --- /dev/null +++ b/src/openai/types/beta/beta_function_shell_tool_param.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_container_auto_param import BetaContainerAutoParam +from .beta_local_environment_param import BetaLocalEnvironmentParam +from .beta_container_reference_param import BetaContainerReferenceParam + +__all__ = ["BetaFunctionShellToolParam", "Environment"] + +Environment: TypeAlias = Union[BetaContainerAutoParam, BetaLocalEnvironmentParam, BetaContainerReferenceParam] + + +class BetaFunctionShellToolParam(TypedDict, total=False): + """A tool that allows the model to execute shell commands.""" + + type: Required[Literal["shell"]] + """The type of the shell tool. Always `shell`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + environment: Optional[Environment] diff --git a/src/openai/types/beta/beta_function_tool.py b/src/openai/types/beta/beta_function_tool.py new file mode 100644 index 0000000000..b9bd6e340e --- /dev/null +++ b/src/openai/types/beta/beta_function_tool.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaFunctionTool"] + + +class BetaFunctionTool(BaseModel): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + """ + + name: str + """The name of the function to call.""" + + parameters: Optional[Dict[str, object]] = None + """A JSON schema object describing the parameters of the function.""" + + strict: Optional[bool] = None + """Whether strict parameter validation is enforced for this function tool.""" + + type: Literal["function"] + """The type of the function tool. Always `function`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + defer_loading: Optional[bool] = None + """Whether this function is deferred and loaded via tool search.""" + + description: Optional[str] = None + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ + + output_schema: Optional[Dict[str, object]] = None + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/src/openai/types/beta/beta_function_tool_param.py b/src/openai/types/beta/beta_function_tool_param.py new file mode 100644 index 0000000000..5a43a15f12 --- /dev/null +++ b/src/openai/types/beta/beta_function_tool_param.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaFunctionToolParam"] + + +class BetaFunctionToolParam(TypedDict, total=False): + """Defines a function in your own code the model can choose to call. + + Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling). + """ + + name: Required[str] + """The name of the function to call.""" + + parameters: Required[Optional[Dict[str, object]]] + """A JSON schema object describing the parameters of the function.""" + + strict: Required[Optional[bool]] + """Whether strict parameter validation is enforced for this function tool.""" + + type: Required[Literal["function"]] + """The type of the function tool. Always `function`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this function is deferred and loaded via tool search.""" + + description: Optional[str] + """A description of the function. + + Used by the model to determine whether or not to call the function. + """ + + output_schema: Optional[Dict[str, object]] + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/src/openai/types/beta/beta_inline_skill.py b/src/openai/types/beta/beta_inline_skill.py new file mode 100644 index 0000000000..45113f354d --- /dev/null +++ b/src/openai/types/beta/beta_inline_skill.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_inline_skill_source import BetaInlineSkillSource + +__all__ = ["BetaInlineSkill"] + + +class BetaInlineSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + source: BetaInlineSkillSource + """Inline skill payload""" + + type: Literal["inline"] + """Defines an inline skill for this request.""" diff --git a/src/openai/types/beta/beta_inline_skill_param.py b/src/openai/types/beta/beta_inline_skill_param.py new file mode 100644 index 0000000000..e0c569db35 --- /dev/null +++ b/src/openai/types/beta/beta_inline_skill_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .beta_inline_skill_source_param import BetaInlineSkillSourceParam + +__all__ = ["BetaInlineSkillParam"] + + +class BetaInlineSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + source: Required[BetaInlineSkillSourceParam] + """Inline skill payload""" + + type: Required[Literal["inline"]] + """Defines an inline skill for this request.""" diff --git a/src/openai/types/beta/beta_inline_skill_source.py b/src/openai/types/beta/beta_inline_skill_source.py new file mode 100644 index 0000000000..64be157527 --- /dev/null +++ b/src/openai/types/beta/beta_inline_skill_source.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaInlineSkillSource"] + + +class BetaInlineSkillSource(BaseModel): + """Inline skill payload""" + + data: str + """Base64-encoded skill zip bundle.""" + + media_type: Literal["application/zip"] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Literal["base64"] + """The type of the inline skill source. Must be `base64`.""" diff --git a/src/openai/types/beta/beta_inline_skill_source_param.py b/src/openai/types/beta/beta_inline_skill_source_param.py new file mode 100644 index 0000000000..0786ba222c --- /dev/null +++ b/src/openai/types/beta/beta_inline_skill_source_param.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaInlineSkillSourceParam"] + + +class BetaInlineSkillSourceParam(TypedDict, total=False): + """Inline skill payload""" + + data: Required[str] + """Base64-encoded skill zip bundle.""" + + media_type: Required[Literal["application/zip"]] + """The media type of the inline skill payload. Must be `application/zip`.""" + + type: Required[Literal["base64"]] + """The type of the inline skill source. Must be `base64`.""" diff --git a/src/openai/types/beta/beta_local_environment.py b/src/openai/types/beta/beta_local_environment.py new file mode 100644 index 0000000000..0b60a6fd8a --- /dev/null +++ b/src/openai/types/beta/beta_local_environment.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_local_skill import BetaLocalSkill + +__all__ = ["BetaLocalEnvironment"] + + +class BetaLocalEnvironment(BaseModel): + type: Literal["local"] + """Use a local computer environment.""" + + skills: Optional[List[BetaLocalSkill]] = None + """An optional list of skills.""" diff --git a/src/openai/types/beta/beta_local_environment_param.py b/src/openai/types/beta/beta_local_environment_param.py new file mode 100644 index 0000000000..da65ca9c86 --- /dev/null +++ b/src/openai/types/beta/beta_local_environment_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .beta_local_skill_param import BetaLocalSkillParam + +__all__ = ["BetaLocalEnvironmentParam"] + + +class BetaLocalEnvironmentParam(TypedDict, total=False): + type: Required[Literal["local"]] + """Use a local computer environment.""" + + skills: Iterable[BetaLocalSkillParam] + """An optional list of skills.""" diff --git a/src/openai/types/beta/beta_local_skill.py b/src/openai/types/beta/beta_local_skill.py new file mode 100644 index 0000000000..074f990759 --- /dev/null +++ b/src/openai/types/beta/beta_local_skill.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaLocalSkill"] + + +class BetaLocalSkill(BaseModel): + description: str + """The description of the skill.""" + + name: str + """The name of the skill.""" + + path: str + """The path to the directory containing the skill.""" diff --git a/src/openai/types/beta/beta_local_skill_param.py b/src/openai/types/beta/beta_local_skill_param.py new file mode 100644 index 0000000000..dbfa134fa1 --- /dev/null +++ b/src/openai/types/beta/beta_local_skill_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BetaLocalSkillParam"] + + +class BetaLocalSkillParam(TypedDict, total=False): + description: Required[str] + """The description of the skill.""" + + name: Required[str] + """The name of the skill.""" + + path: Required[str] + """The path to the directory containing the skill.""" diff --git a/src/openai/types/beta/beta_namespace_tool.py b/src/openai/types/beta/beta_namespace_tool.py new file mode 100644 index 0000000000..3949a55ca8 --- /dev/null +++ b/src/openai/types/beta/beta_namespace_tool.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_custom_tool import BetaCustomTool + +__all__ = ["BetaNamespaceTool", "Tool", "ToolFunction"] + + +class ToolFunction(BaseModel): + name: str + + type: Literal["function"] + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + defer_loading: Optional[bool] = None + """Whether this function should be deferred and discovered via tool search.""" + + description: Optional[str] = None + + output_schema: Optional[Dict[str, object]] = None + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + + parameters: Optional[object] = None + + strict: Optional[bool] = None + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ + + +Tool: TypeAlias = Annotated[Union[ToolFunction, BetaCustomTool], PropertyInfo(discriminator="type")] + + +class BetaNamespaceTool(BaseModel): + """Groups function/custom tools under a shared namespace.""" + + description: str + """A description of the namespace shown to the model.""" + + name: str + """The namespace name used in tool calls (for example, `crm`).""" + + tools: List[Tool] + """The function/custom tools available inside this namespace.""" + + type: Literal["namespace"] + """The type of the tool. Always `namespace`.""" diff --git a/src/openai/types/beta/beta_namespace_tool_param.py b/src/openai/types/beta/beta_namespace_tool_param.py new file mode 100644 index 0000000000..752a61f0c6 --- /dev/null +++ b/src/openai/types/beta/beta_namespace_tool_param.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_custom_tool_param import BetaCustomToolParam + +__all__ = ["BetaNamespaceToolParam", "Tool", "ToolFunction"] + + +class ToolFunction(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["function"]] + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + defer_loading: bool + """Whether this function should be deferred and discovered via tool search.""" + + description: Optional[str] + + output_schema: Optional[Dict[str, object]] + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + + parameters: Optional[object] + + strict: Optional[bool] + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ + + +Tool: TypeAlias = Union[ToolFunction, BetaCustomToolParam] + + +class BetaNamespaceToolParam(TypedDict, total=False): + """Groups function/custom tools under a shared namespace.""" + + description: Required[str] + """A description of the namespace shown to the model.""" + + name: Required[str] + """The namespace name used in tool calls (for example, `crm`).""" + + tools: Required[Iterable[Tool]] + """The function/custom tools available inside this namespace.""" + + type: Required[Literal["namespace"]] + """The type of the tool. Always `namespace`.""" diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py new file mode 100644 index 0000000000..eb50bcbcd0 --- /dev/null +++ b/src/openai/types/beta/beta_response.py @@ -0,0 +1,616 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_response_error import BetaResponseError +from .beta_response_usage import BetaResponseUsage +from .beta_response_prompt import BetaResponsePrompt +from .beta_response_status import BetaResponseStatus +from .beta_tool_choice_mcp import BetaToolChoiceMcp +from .beta_tool_choice_shell import BetaToolChoiceShell +from .beta_tool_choice_types import BetaToolChoiceTypes +from .beta_tool_choice_custom import BetaToolChoiceCustom +from .beta_response_input_item import BetaResponseInputItem +from .beta_tool_choice_allowed import BetaToolChoiceAllowed +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_output_item import BetaResponseOutputItem +from .beta_response_text_config import BetaResponseTextConfig +from .beta_tool_choice_function import BetaToolChoiceFunction +from .beta_tool_choice_apply_patch import BetaToolChoiceApplyPatch + +__all__ = [ + "BetaResponse", + "IncompleteDetails", + "ToolChoice", + "ToolChoiceBetaSpecificProgrammaticToolCallingParam", + "Conversation", + "Moderation", + "ModerationInput", + "ModerationInputModerationResult", + "ModerationInputError", + "ModerationOutput", + "ModerationOutputModerationResult", + "ModerationOutputError", + "PromptCacheOptions", + "Reasoning", +] + + +class IncompleteDetails(BaseModel): + """Details about why the response is incomplete.""" + + reason: Optional[Literal["max_output_tokens", "content_filter"]] = None + """The reason why the response is incomplete.""" + + +class ToolChoiceBetaSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowed, + BetaToolChoiceTypes, + BetaToolChoiceFunction, + BetaToolChoiceMcp, + BetaToolChoiceCustom, + ToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatch, + BetaToolChoiceShell, +] + + +class Conversation(BaseModel): + """The conversation that this response belonged to. + + Input items and output items from this response were automatically added to this conversation. + """ + + id: str + """The unique ID of the conversation that this response was associated with.""" + + +class ModerationInputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationInputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationInput: TypeAlias = Annotated[ + Union[ModerationInputModerationResult, ModerationInputError], PropertyInfo(discriminator="type") +] + + +class ModerationOutputModerationResult(BaseModel): + """A moderation result produced for the response input or output.""" + + categories: Dict[str, bool] + """ + A dictionary of moderation categories to booleans, True if the input is flagged + under this category. + """ + + category_applied_input_types: Dict[str, List[Literal["text", "image"]]] + """Which modalities of input are reflected by the score for each category.""" + + category_scores: Dict[str, float] + """A dictionary of moderation categories to scores.""" + + flagged: bool + """A boolean indicating whether the content was flagged by any category.""" + + model: str + """The moderation model that produced this result.""" + + type: Literal["moderation_result"] + """ + The object type, which was always `moderation_result` for successful moderation + results. + """ + + +class ModerationOutputError(BaseModel): + """An error produced while attempting moderation for the response input or output.""" + + code: str + """The error code.""" + + message: str + """The error message.""" + + type: Literal["error"] + """The object type, which was always `error` for moderation failures.""" + + +ModerationOutput: TypeAlias = Annotated[ + Union[ModerationOutputModerationResult, ModerationOutputError], PropertyInfo(discriminator="type") +] + + +class Moderation(BaseModel): + """ + Moderation results for the response input and output, if moderated completions were requested. + """ + + input: ModerationInput + """Moderation for the response input.""" + + output: ModerationOutput + """Moderation for the response output.""" + + +class PromptCacheOptions(BaseModel): + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + + mode: Literal["implicit", "explicit"] + """Whether implicit prompt-cache breakpoints were enabled.""" + + ttl: Literal["30m"] + """The minimum lifetime applied to each cache breakpoint.""" + + +class Reasoning(BaseModel): + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] = None + """ + Controls which reasoning items are rendered back to the model on later turns. + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"], None] = None + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] = None + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class BetaResponse(BaseModel): + id: str + """Unique identifier for this Response.""" + + created_at: float + """Unix timestamp (in seconds) of when this Response was created.""" + + error: Optional[BetaResponseError] = None + """An error object returned when the model fails to generate a Response.""" + + incomplete_details: Optional[IncompleteDetails] = None + """Details about why the response is incomplete.""" + + instructions: Union[str, List[BetaResponseInputItem], None] = None + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + metadata: Optional[Dict[str, str]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + object: Literal["response"] + """The object type of this resource - always set to `response`.""" + + output: List[BetaResponseOutputItem] + """An array of content items generated by the model. + + - The length and order of items in the `output` array is dependent on the + model's response. + - Rather than accessing the first item in the `output` array and assuming it's + an `assistant` message with the content generated by the model, you might + consider using the `output_text` property where supported in SDKs. + """ + + parallel_tool_calls: bool + """Whether to allow the model to run tool calls in parallel.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: List[BetaTool] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + background: Optional[bool] = None + """ + Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + """ + + completed_at: Optional[float] = None + """ + Unix timestamp (in seconds) of when this Response was completed. Only present + when the status is `completed`. + """ + + conversation: Optional[Conversation] = None + """The conversation that this response belonged to. + + Input items and output items from this response were automatically added to this + conversation. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] = None + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + moderation: Optional[Moderation] = None + """ + Moderation results for the response input and output, if moderated completions + were requested. + """ + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + + prompt_cache_options: Optional[PromptCacheOptions] = None + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] = None + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + status: Optional[BetaResponseStatus] = None + """The status of the response generation. + + One of `completed`, `failed`, `in_progress`, `cancelled`, `queued`, or + `incomplete`. + """ + + text: Optional[BetaResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + top_logprobs: Optional[int] = None + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + usage: Optional[BetaResponseUsage] = None + """ + Represents token usage details including input tokens, output tokens, a + breakdown of output tokens, and the total tokens used. + """ + + user: Optional[str] = None + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ diff --git a/src/openai/types/beta/beta_response_apply_patch_tool_call.py b/src/openai/types/beta/beta_response_apply_patch_tool_call.py new file mode 100644 index 0000000000..3fa0bcdff3 --- /dev/null +++ b/src/openai/types/beta/beta_response_apply_patch_tool_call.py @@ -0,0 +1,115 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseApplyPatchToolCall", + "Operation", + "OperationCreateFile", + "OperationDeleteFile", + "OperationUpdateFile", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class OperationCreateFile(BaseModel): + """Instruction describing how to create a file via the apply_patch tool.""" + + diff: str + """Diff to apply.""" + + path: str + """Path of the file to create.""" + + type: Literal["create_file"] + """Create a new file with the provided diff.""" + + +class OperationDeleteFile(BaseModel): + """Instruction describing how to delete a file via the apply_patch tool.""" + + path: str + """Path of the file to delete.""" + + type: Literal["delete_file"] + """Delete the specified file.""" + + +class OperationUpdateFile(BaseModel): + """Instruction describing how to update a file via the apply_patch tool.""" + + diff: str + """Diff to apply.""" + + path: str + """Path of the file to update.""" + + type: Literal["update_file"] + """Update an existing file with the provided diff.""" + + +Operation: TypeAlias = Annotated[ + Union[OperationCreateFile, OperationDeleteFile, OperationUpdateFile], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseApplyPatchToolCall(BaseModel): + """A tool call that applies file diffs by creating, deleting, or updating files.""" + + id: str + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Operation + """ + One of the create_file, delete_file, or update_file operations applied via + apply_patch. + """ + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" diff --git a/src/openai/types/beta/beta_response_apply_patch_tool_call_output.py b/src/openai/types/beta/beta_response_apply_patch_tool_call_output.py new file mode 100644 index 0000000000..2b653a73e1 --- /dev/null +++ b/src/openai/types/beta/beta_response_apply_patch_tool_call_output.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseApplyPatchToolCallOutput", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseApplyPatchToolCallOutput(BaseModel): + """The output emitted by an apply patch tool call.""" + + id: str + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call output.""" + + output: Optional[str] = None + """Optional textual output returned by the apply patch tool.""" diff --git a/src/openai/types/beta/beta_response_audio_delta_event.py b/src/openai/types/beta/beta_response_audio_delta_event.py new file mode 100644 index 0000000000..9ae1f370ef --- /dev/null +++ b/src/openai/types/beta/beta_response_audio_delta_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioDeltaEvent(BaseModel): + """Emitted when there is a partial audio response.""" + + delta: str + """A chunk of Base64 encoded response audio bytes.""" + + sequence_number: int + """A sequence number for this chunk of the stream response.""" + + type: Literal["response.audio.delta"] + """The type of the event. Always `response.audio.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_audio_done_event.py b/src/openai/types/beta/beta_response_audio_done_event.py new file mode 100644 index 0000000000..7b4a8de20d --- /dev/null +++ b/src/openai/types/beta/beta_response_audio_done_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioDoneEvent(BaseModel): + """Emitted when the audio response is complete.""" + + sequence_number: int + """The sequence number of the delta.""" + + type: Literal["response.audio.done"] + """The type of the event. Always `response.audio.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_audio_transcript_delta_event.py b/src/openai/types/beta/beta_response_audio_transcript_delta_event.py new file mode 100644 index 0000000000..d23877676a --- /dev/null +++ b/src/openai/types/beta/beta_response_audio_transcript_delta_event.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioTranscriptDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioTranscriptDeltaEvent(BaseModel): + """Emitted when there is a partial transcript of audio.""" + + delta: str + """The partial transcript of the audio response.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.audio.transcript.delta"] + """The type of the event. Always `response.audio.transcript.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_audio_transcript_done_event.py b/src/openai/types/beta/beta_response_audio_transcript_done_event.py new file mode 100644 index 0000000000..f52c0e383c --- /dev/null +++ b/src/openai/types/beta/beta_response_audio_transcript_done_event.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseAudioTranscriptDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseAudioTranscriptDoneEvent(BaseModel): + """Emitted when the full audio transcript is completed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.audio.transcript.done"] + """The type of the event. Always `response.audio.transcript.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py new file mode 100644 index 0000000000..cdcf5bbcd5 --- /dev/null +++ b/src/openai/types/beta/beta_response_code_interpreter_call_code_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallCodeDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallCodeDeltaEvent(BaseModel): + """Emitted when a partial code snippet is streamed by the code interpreter.""" + + delta: str + """The partial code snippet being streamed by the code interpreter.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code is being + streamed. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call_code.delta"] + """The type of the event. Always `response.code_interpreter_call_code.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py new file mode 100644 index 0000000000..d685753b5d --- /dev/null +++ b/src/openai/types/beta/beta_response_code_interpreter_call_code_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallCodeDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallCodeDoneEvent(BaseModel): + """Emitted when the code snippet is finalized by the code interpreter.""" + + code: str + """The final code snippet output by the code interpreter.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """The index of the output item in the response for which the code is finalized.""" + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call_code.done"] + """The type of the event. Always `response.code_interpreter_call_code.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py new file mode 100644 index 0000000000..272d5d5ab5 --- /dev/null +++ b/src/openai/types/beta/beta_response_code_interpreter_call_completed_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallCompletedEvent(BaseModel): + """Emitted when the code interpreter call is completed.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter call + is completed. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.completed"] + """The type of the event. Always `response.code_interpreter_call.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py new file mode 100644 index 0000000000..2d01717e8f --- /dev/null +++ b/src/openai/types/beta/beta_response_code_interpreter_call_in_progress_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallInProgressEvent(BaseModel): + """Emitted when a code interpreter call is in progress.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter call + is in progress. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.in_progress"] + """The type of the event. Always `response.code_interpreter_call.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py b/src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py new file mode 100644 index 0000000000..d92938ef45 --- /dev/null +++ b/src/openai/types/beta/beta_response_code_interpreter_call_interpreting_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterCallInterpretingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterCallInterpretingEvent(BaseModel): + """Emitted when the code interpreter is actively interpreting the code snippet.""" + + item_id: str + """The unique identifier of the code interpreter tool call item.""" + + output_index: int + """ + The index of the output item in the response for which the code interpreter is + interpreting code. + """ + + sequence_number: int + """The sequence number of this event, used to order streaming events.""" + + type: Literal["response.code_interpreter_call.interpreting"] + """The type of the event. Always `response.code_interpreter_call.interpreting`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_code_interpreter_tool_call.py b/src/openai/types/beta/beta_response_code_interpreter_tool_call.py new file mode 100644 index 0000000000..380f289577 --- /dev/null +++ b/src/openai/types/beta/beta_response_code_interpreter_tool_call.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseCodeInterpreterToolCall", "Output", "OutputLogs", "OutputImage", "Agent"] + + +class OutputLogs(BaseModel): + """The logs output from the code interpreter.""" + + logs: str + """The logs output from the code interpreter.""" + + type: Literal["logs"] + """The type of the output. Always `logs`.""" + + +class OutputImage(BaseModel): + """The image output from the code interpreter.""" + + type: Literal["image"] + """The type of the output. Always `image`.""" + + url: str + """The URL of the image output from the code interpreter.""" + + +Output: TypeAlias = Annotated[Union[OutputLogs, OutputImage], PropertyInfo(discriminator="type")] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterToolCall(BaseModel): + """A tool call to run code.""" + + id: str + """The unique ID of the code interpreter tool call.""" + + code: Optional[str] = None + """The code to run, or null if not available.""" + + container_id: str + """The ID of the container used to run the code.""" + + outputs: Optional[List[Output]] = None + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. + """ + + status: Literal["in_progress", "completed", "incomplete", "interpreting", "failed"] + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ + + type: Literal["code_interpreter_call"] + """The type of the code interpreter tool call. Always `code_interpreter_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py b/src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py new file mode 100644 index 0000000000..0ed7f9207e --- /dev/null +++ b/src/openai/types/beta/beta_response_code_interpreter_tool_call_param.py @@ -0,0 +1,70 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseCodeInterpreterToolCallParam", "Output", "OutputLogs", "OutputImage", "Agent"] + + +class OutputLogs(TypedDict, total=False): + """The logs output from the code interpreter.""" + + logs: Required[str] + """The logs output from the code interpreter.""" + + type: Required[Literal["logs"]] + """The type of the output. Always `logs`.""" + + +class OutputImage(TypedDict, total=False): + """The image output from the code interpreter.""" + + type: Required[Literal["image"]] + """The type of the output. Always `image`.""" + + url: Required[str] + """The URL of the image output from the code interpreter.""" + + +Output: TypeAlias = Union[OutputLogs, OutputImage] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCodeInterpreterToolCallParam(TypedDict, total=False): + """A tool call to run code.""" + + id: Required[str] + """The unique ID of the code interpreter tool call.""" + + code: Required[Optional[str]] + """The code to run, or null if not available.""" + + container_id: Required[str] + """The ID of the container used to run the code.""" + + outputs: Required[Optional[Iterable[Output]]] + """ + The outputs generated by the code interpreter, such as logs or images. Can be + null if no outputs are available. + """ + + status: Required[Literal["in_progress", "completed", "incomplete", "interpreting", "failed"]] + """The status of the code interpreter tool call. + + Valid values are `in_progress`, `completed`, `incomplete`, `interpreting`, and + `failed`. + """ + + type: Required[Literal["code_interpreter_call"]] + """The type of the code interpreter tool call. Always `code_interpreter_call`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_compaction_item.py b/src/openai/types/beta/beta_response_compaction_item.py new file mode 100644 index 0000000000..3e76634842 --- /dev/null +++ b/src/openai/types/beta/beta_response_compaction_item.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCompactionItem", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompactionItem(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact). + """ + + id: str + """The unique ID of the compaction item.""" + + encrypted_content: str + """The encrypted content that was produced by compaction.""" + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_compaction_item_param.py b/src/openai/types/beta/beta_response_compaction_item_param.py new file mode 100644 index 0000000000..3d98270201 --- /dev/null +++ b/src/openai/types/beta/beta_response_compaction_item_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCompactionItemParam", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompactionItemParam(BaseModel): + """ + A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact). + """ + + encrypted_content: str + """The encrypted content of the compaction summary.""" + + type: Literal["compaction"] + """The type of the item. Always `compaction`.""" + + id: Optional[str] = None + """The ID of the compaction item.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_compaction_item_param_param.py b/src/openai/types/beta/beta_response_compaction_item_param_param.py new file mode 100644 index 0000000000..b66c1baba8 --- /dev/null +++ b/src/openai/types/beta/beta_response_compaction_item_param_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseCompactionItemParamParam", "Agent"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompactionItemParamParam(TypedDict, total=False): + """ + A compaction item generated by the [`v1/responses/compact` API](https://platform.openai.com/docs/api-reference/responses/compact). + """ + + encrypted_content: Required[str] + """The encrypted content of the compaction summary.""" + + type: Required[Literal["compaction"]] + """The type of the item. Always `compaction`.""" + + id: Optional[str] + """The ID of the compaction item.""" + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_completed_event.py b/src/openai/types/beta/beta_response_completed_event.py new file mode 100644 index 0000000000..bc07df52d9 --- /dev/null +++ b/src/openai/types/beta/beta_response_completed_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCompletedEvent(BaseModel): + """Emitted when the model response is complete.""" + + response: BetaResponse + """Properties of the completed response.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.completed"] + """The type of the event. Always `response.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_computer_tool_call.py b/src/openai/types/beta/beta_response_computer_tool_call.py new file mode 100644 index 0000000000..f04ea43fae --- /dev/null +++ b/src/openai/types/beta/beta_response_computer_tool_call.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_computer_action import BetaComputerAction +from .beta_computer_action_list import BetaComputerActionList + +__all__ = ["BetaResponseComputerToolCall", "PendingSafetyCheck", "Agent"] + + +class PendingSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseComputerToolCall(BaseModel): + """A tool call to a computer use tool. + + See the + [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information. + """ + + id: str + """The unique ID of the computer call.""" + + call_id: str + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: List[PendingSafetyCheck] + """The pending safety checks for the computer call.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["computer_call"] + """The type of the computer call. Always `computer_call`.""" + + action: Optional[BetaComputerAction] = None + """A click action.""" + + actions: Optional[BetaComputerActionList] = None + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_computer_tool_call_output_item.py b/src/openai/types/beta/beta_response_computer_tool_call_output_item.py new file mode 100644 index 0000000000..c94a88b320 --- /dev/null +++ b/src/openai/types/beta/beta_response_computer_tool_call_output_item.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_computer_tool_call_output_screenshot import BetaResponseComputerToolCallOutputScreenshot + +__all__ = ["BetaResponseComputerToolCallOutputItem", "AcknowledgedSafetyCheck", "Agent"] + + +class AcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseComputerToolCallOutputItem(BaseModel): + id: str + """The unique ID of the computer call tool output.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: BetaResponseComputerToolCallOutputScreenshot + """A computer screenshot image used with the computer use tool.""" + + status: Literal["completed", "incomplete", "failed", "in_progress"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + acknowledged_safety_checks: Optional[List[AcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py new file mode 100644 index 0000000000..fbb3844eea --- /dev/null +++ b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseComputerToolCallOutputScreenshot"] + + +class BetaResponseComputerToolCallOutputScreenshot(BaseModel): + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" diff --git a/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py new file mode 100644 index 0000000000..9697e97373 --- /dev/null +++ b/src/openai/types/beta/beta_response_computer_tool_call_output_screenshot_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseComputerToolCallOutputScreenshotParam"] + + +class BetaResponseComputerToolCallOutputScreenshotParam(TypedDict, total=False): + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_screenshot"]] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + file_id: str + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: str + """The URL of the screenshot image.""" diff --git a/src/openai/types/beta/beta_response_computer_tool_call_param.py b/src/openai/types/beta/beta_response_computer_tool_call_param.py new file mode 100644 index 0000000000..0072a88a44 --- /dev/null +++ b/src/openai/types/beta/beta_response_computer_tool_call_param.py @@ -0,0 +1,70 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_computer_action_param import BetaComputerActionParam +from .beta_computer_action_list_param import BetaComputerActionListParam + +__all__ = ["BetaResponseComputerToolCallParam", "PendingSafetyCheck", "Agent"] + + +class PendingSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseComputerToolCallParam(TypedDict, total=False): + """A tool call to a computer use tool. + + See the + [computer use guide](https://platform.openai.com/docs/guides/tools-computer-use) for more information. + """ + + id: Required[str] + """The unique ID of the computer call.""" + + call_id: Required[str] + """An identifier used when responding to the tool call with output.""" + + pending_safety_checks: Required[Iterable[PendingSafetyCheck]] + """The pending safety checks for the computer call.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Required[Literal["computer_call"]] + """The type of the computer call. Always `computer_call`.""" + + action: BetaComputerActionParam + """A click action.""" + + actions: BetaComputerActionListParam + """Flattened batched actions for `computer_use`. + + Each action includes an `type` discriminator and action-specific fields. + """ + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_container_reference.py b/src/openai/types/beta/beta_response_container_reference.py new file mode 100644 index 0000000000..e65ce1fe3d --- /dev/null +++ b/src/openai/types/beta/beta_response_container_reference.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseContainerReference"] + + +class BetaResponseContainerReference(BaseModel): + """Represents a container created with /v1/containers.""" + + container_id: str + + type: Literal["container_reference"] + """The environment type. Always `container_reference`.""" diff --git a/src/openai/types/beta/beta_response_content_part_added_event.py b/src/openai/types/beta/beta_response_content_part_added_event.py new file mode 100644 index 0000000000..1a66e4da0b --- /dev/null +++ b/src/openai/types/beta/beta_response_content_part_added_event.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_refusal import BetaResponseOutputRefusal + +__all__ = ["BetaResponseContentPartAddedEvent", "Part", "PartReasoningText", "Agent"] + + +class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[BetaResponseOutputText, BetaResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseContentPartAddedEvent(BaseModel): + """Emitted when a new content part is added.""" + + content_index: int + """The index of the content part that was added.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.content_part.added"] + """The type of the event. Always `response.content_part.added`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_content_part_done_event.py b/src/openai/types/beta/beta_response_content_part_done_event.py new file mode 100644 index 0000000000..d79d1db23e --- /dev/null +++ b/src/openai/types/beta/beta_response_content_part_done_event.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_refusal import BetaResponseOutputRefusal + +__all__ = ["BetaResponseContentPartDoneEvent", "Part", "PartReasoningText", "Agent"] + + +class PartReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +Part: TypeAlias = Annotated[ + Union[BetaResponseOutputText, BetaResponseOutputRefusal, PartReasoningText], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseContentPartDoneEvent(BaseModel): + """Emitted when a content part is done.""" + + content_index: int + """The index of the content part that is done.""" + + item_id: str + """The ID of the output item that the content part was added to.""" + + output_index: int + """The index of the output item that the content part was added to.""" + + part: Part + """The content part that is done.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.content_part.done"] + """The type of the event. Always `response.content_part.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_conversation_param.py b/src/openai/types/beta/beta_response_conversation_param.py new file mode 100644 index 0000000000..565462c4ba --- /dev/null +++ b/src/openai/types/beta/beta_response_conversation_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaResponseConversationParam"] + + +class BetaResponseConversationParam(BaseModel): + """The conversation that this response belongs to.""" + + id: str + """The unique ID of the conversation.""" diff --git a/src/openai/types/beta/beta_response_conversation_param_param.py b/src/openai/types/beta/beta_response_conversation_param_param.py new file mode 100644 index 0000000000..c70bc81098 --- /dev/null +++ b/src/openai/types/beta/beta_response_conversation_param_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["BetaResponseConversationParamParam"] + + +class BetaResponseConversationParamParam(TypedDict, total=False): + """The conversation that this response belongs to.""" + + id: Required[str] + """The unique ID of the conversation.""" diff --git a/src/openai/types/beta/beta_response_created_event.py b/src/openai/types/beta/beta_response_created_event.py new file mode 100644 index 0000000000..4a7097d292 --- /dev/null +++ b/src/openai/types/beta/beta_response_created_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseCreatedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCreatedEvent(BaseModel): + """An event that is emitted when a response is created.""" + + response: BetaResponse + """The response that was created.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.created"] + """The type of the event. Always `response.created`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call.py b/src/openai/types/beta/beta_response_custom_tool_call.py new file mode 100644 index 0000000000..0cb2c13396 --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseCustomToolCall", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseCustomToolCall(BaseModel): + """A call to a custom tool created by the model.""" + + call_id: str + """An identifier used to map this custom tool call to a tool call output.""" + + input: str + """The input for the custom tool call generated by the model.""" + + name: str + """The name of the custom tool being called.""" + + type: Literal["custom_tool_call"] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call in the OpenAI platform.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + namespace: Optional[str] = None + """The namespace of the custom tool being called.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py b/src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py new file mode 100644 index 0000000000..09676873c6 --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call_input_delta_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCustomToolCallInputDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCustomToolCallInputDeltaEvent(BaseModel): + """Event representing a delta (partial update) to the input of a custom tool call.""" + + delta: str + """The incremental input data (delta) for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this delta applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.delta"] + """The event type identifier.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py b/src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py new file mode 100644 index 0000000000..b8eac13b21 --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call_input_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseCustomToolCallInputDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseCustomToolCallInputDoneEvent(BaseModel): + """Event indicating that input for a custom tool call is complete.""" + + input: str + """The complete input data for the custom tool call.""" + + item_id: str + """Unique identifier for the API item associated with this event.""" + + output_index: int + """The index of the output this event applies to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.custom_tool_call_input.done"] + """The event type identifier.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call_item.py b/src/openai/types/beta/beta_response_custom_tool_call_item.py new file mode 100644 index 0000000000..74c31845ec --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .beta_response_custom_tool_call import BetaResponseCustomToolCall + +__all__ = ["BetaResponseCustomToolCallItem"] + + +class BetaResponseCustomToolCallItem(BetaResponseCustomToolCall): + """A call to a custom tool created by the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call_output.py b/src/openai/types/beta/beta_response_custom_tool_call_output.py new file mode 100644 index 0000000000..ad2dc830b6 --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call_output.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = [ + "BetaResponseCustomToolCallOutput", + "OutputOutputContentList", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseCustomToolCallOutput(BaseModel): + """The output of a custom tool call from your code, being sent back to the model.""" + + call_id: str + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: Union[str, List[OutputOutputContentList]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ + + type: Literal["custom_tool_call_output"] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: Optional[str] = None + """The unique ID of the custom tool call output in the OpenAI platform.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call_output_item.py b/src/openai/types/beta/beta_response_custom_tool_call_output_item.py new file mode 100644 index 0000000000..018140f6d4 --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call_output_item.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput + +__all__ = ["BetaResponseCustomToolCallOutputItem"] + + +class BetaResponseCustomToolCallOutputItem(BetaResponseCustomToolCallOutput): + """The output of a custom tool call from your code, being sent back to the model.""" + + id: str # type: ignore + """The unique ID of the custom tool call output item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call_output_param.py b/src/openai/types/beta/beta_response_custom_tool_call_output_param.py new file mode 100644 index 0000000000..320be64d69 --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call_output_param.py @@ -0,0 +1,71 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = [ + "BetaResponseCustomToolCallOutputParam", + "OutputOutputContentList", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + +OutputOutputContentList: TypeAlias = Union[ + BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam +] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +class BetaResponseCustomToolCallOutputParam(TypedDict, total=False): + """The output of a custom tool call from your code, being sent back to the model.""" + + call_id: Required[str] + """The call ID, used to map this custom tool call output to a custom tool call.""" + + output: Required[Union[str, Iterable[OutputOutputContentList]]] + """ + The output from the custom tool call generated by your code. Can be a string or + an list of output content. + """ + + type: Required[Literal["custom_tool_call_output"]] + """The type of the custom tool call output. Always `custom_tool_call_output`.""" + + id: str + """The unique ID of the custom tool call output in the OpenAI platform.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" diff --git a/src/openai/types/beta/beta_response_custom_tool_call_param.py b/src/openai/types/beta/beta_response_custom_tool_call_param.py new file mode 100644 index 0000000000..0681725e0a --- /dev/null +++ b/src/openai/types/beta/beta_response_custom_tool_call_param.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseCustomToolCallParam", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +class BetaResponseCustomToolCallParam(TypedDict, total=False): + """A call to a custom tool created by the model.""" + + call_id: Required[str] + """An identifier used to map this custom tool call to a tool call output.""" + + input: Required[str] + """The input for the custom tool call generated by the model.""" + + name: Required[str] + """The name of the custom tool being called.""" + + type: Required[Literal["custom_tool_call"]] + """The type of the custom tool call. Always `custom_tool_call`.""" + + id: str + """The unique ID of the custom tool call in the OpenAI platform.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" + + namespace: str + """The namespace of the custom tool being called.""" diff --git a/src/openai/types/beta/beta_response_error.py b/src/openai/types/beta/beta_response_error.py new file mode 100644 index 0000000000..c725765d63 --- /dev/null +++ b/src/openai/types/beta/beta_response_error.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseError"] + + +class BetaResponseError(BaseModel): + """An error object returned when the model fails to generate a Response.""" + + code: Literal[ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "bio_policy", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found", + ] + """The error code for the response.""" + + message: str + """A human-readable description of the error.""" diff --git a/src/openai/types/beta/beta_response_error_event.py b/src/openai/types/beta/beta_response_error_event.py new file mode 100644 index 0000000000..b1f8b7923a --- /dev/null +++ b/src/openai/types/beta/beta_response_error_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseErrorEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseErrorEvent(BaseModel): + """Emitted when an error occurs.""" + + code: Optional[str] = None + """The error code.""" + + message: str + """The error message.""" + + param: Optional[str] = None + """The error parameter.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["error"] + """The type of the event. Always `error`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_failed_event.py b/src/openai/types/beta/beta_response_failed_event.py new file mode 100644 index 0000000000..1abe880c63 --- /dev/null +++ b/src/openai/types/beta/beta_response_failed_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseFailedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFailedEvent(BaseModel): + """An event that is emitted when a response fails.""" + + response: BetaResponse + """The response that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.failed"] + """The type of the event. Always `response.failed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_file_search_call_completed_event.py b/src/openai/types/beta/beta_response_file_search_call_completed_event.py new file mode 100644 index 0000000000..3c4f7faa1c --- /dev/null +++ b/src/openai/types/beta/beta_response_file_search_call_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFileSearchCallCompletedEvent(BaseModel): + """Emitted when a file search call is completed (results found).""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.completed"] + """The type of the event. Always `response.file_search_call.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_file_search_call_in_progress_event.py b/src/openai/types/beta/beta_response_file_search_call_in_progress_event.py new file mode 100644 index 0000000000..b75513f8e0 --- /dev/null +++ b/src/openai/types/beta/beta_response_file_search_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFileSearchCallInProgressEvent(BaseModel): + """Emitted when a file search call is initiated.""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is initiated.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.in_progress"] + """The type of the event. Always `response.file_search_call.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_file_search_call_searching_event.py b/src/openai/types/beta/beta_response_file_search_call_searching_event.py new file mode 100644 index 0000000000..671bb75be5 --- /dev/null +++ b/src/openai/types/beta/beta_response_file_search_call_searching_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchCallSearchingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFileSearchCallSearchingEvent(BaseModel): + """Emitted when a file search is currently searching.""" + + item_id: str + """The ID of the output item that the file search call is initiated.""" + + output_index: int + """The index of the output item that the file search call is searching.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.file_search_call.searching"] + """The type of the event. Always `response.file_search_call.searching`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_file_search_tool_call.py b/src/openai/types/beta/beta_response_file_search_tool_call.py new file mode 100644 index 0000000000..c621c62dcd --- /dev/null +++ b/src/openai/types/beta/beta_response_file_search_tool_call.py @@ -0,0 +1,67 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFileSearchToolCall", "Agent", "Result"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Result(BaseModel): + attributes: Optional[Dict[str, Union[str, float, bool]]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: Optional[str] = None + """The unique ID of the file.""" + + filename: Optional[str] = None + """The name of the file.""" + + score: Optional[float] = None + """The relevance score of the file - a value between 0 and 1.""" + + text: Optional[str] = None + """The text that was retrieved from the file.""" + + +class BetaResponseFileSearchToolCall(BaseModel): + """The results of a file search tool call. + + See the + [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information. + """ + + id: str + """The unique ID of the file search tool call.""" + + queries: List[str] + """The queries used to search for files.""" + + status: Literal["in_progress", "searching", "completed", "incomplete", "failed"] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Literal["file_search_call"] + """The type of the file search tool call. Always `file_search_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + results: Optional[List[Result]] = None + """The results of the file search tool call.""" diff --git a/src/openai/types/beta/beta_response_file_search_tool_call_param.py b/src/openai/types/beta/beta_response_file_search_tool_call_param.py new file mode 100644 index 0000000000..60fadaaf89 --- /dev/null +++ b/src/openai/types/beta/beta_response_file_search_tool_call_param.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["BetaResponseFileSearchToolCallParam", "Agent", "Result"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Result(TypedDict, total=False): + attributes: Optional[Dict[str, Union[str, float, bool]]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. Keys are + strings with a maximum length of 64 characters. Values are strings with a + maximum length of 512 characters, booleans, or numbers. + """ + + file_id: str + """The unique ID of the file.""" + + filename: str + """The name of the file.""" + + score: float + """The relevance score of the file - a value between 0 and 1.""" + + text: str + """The text that was retrieved from the file.""" + + +class BetaResponseFileSearchToolCallParam(TypedDict, total=False): + """The results of a file search tool call. + + See the + [file search guide](https://platform.openai.com/docs/guides/tools-file-search) for more information. + """ + + id: Required[str] + """The unique ID of the file search tool call.""" + + queries: Required[SequenceNotStr[str]] + """The queries used to search for files.""" + + status: Required[Literal["in_progress", "searching", "completed", "incomplete", "failed"]] + """The status of the file search tool call. + + One of `in_progress`, `searching`, `incomplete` or `failed`, + """ + + type: Required[Literal["file_search_call"]] + """The type of the file search tool call. Always `file_search_call`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + results: Optional[Iterable[Result]] + """The results of the file search tool call.""" diff --git a/src/openai/types/beta/beta_response_format_text_config.py b/src/openai/types/beta/beta_response_format_text_config.py new file mode 100644 index 0000000000..c9dc25f6c2 --- /dev/null +++ b/src/openai/types/beta/beta_response_format_text_config.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_format_text_json_schema_config import BetaResponseFormatTextJSONSchemaConfig + +__all__ = ["BetaResponseFormatTextConfig", "Text", "JSONObject"] + + +class Text(BaseModel): + """Default response format. Used to generate text responses.""" + + type: Literal["text"] + """The type of response format being defined. Always `text`.""" + + +class JSONObject(BaseModel): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + + type: Literal["json_object"] + """The type of response format being defined. Always `json_object`.""" + + +BetaResponseFormatTextConfig: TypeAlias = Annotated[ + Union[Text, BetaResponseFormatTextJSONSchemaConfig, JSONObject], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/beta_response_format_text_config_param.py b/src/openai/types/beta/beta_response_format_text_config_param.py new file mode 100644 index 0000000000..e44ffe7da6 --- /dev/null +++ b/src/openai/types/beta/beta_response_format_text_config_param.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_response_format_text_json_schema_config_param import BetaResponseFormatTextJSONSchemaConfigParam + +__all__ = ["BetaResponseFormatTextConfigParam", "Text", "JSONObject"] + + +class Text(TypedDict, total=False): + """Default response format. Used to generate text responses.""" + + type: Required[Literal["text"]] + """The type of response format being defined. Always `text`.""" + + +class JSONObject(TypedDict, total=False): + """JSON object response format. + + An older method of generating JSON responses. + Using `json_schema` is recommended for models that support it. Note that the + model will not generate JSON without a system or user message instructing it + to do so. + """ + + type: Required[Literal["json_object"]] + """The type of response format being defined. Always `json_object`.""" + + +BetaResponseFormatTextConfigParam: TypeAlias = Union[Text, BetaResponseFormatTextJSONSchemaConfigParam, JSONObject] diff --git a/src/openai/types/beta/beta_response_format_text_json_schema_config.py b/src/openai/types/beta/beta_response_format_text_json_schema_config.py new file mode 100644 index 0000000000..292a112e1e --- /dev/null +++ b/src/openai/types/beta/beta_response_format_text_json_schema_config.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["BetaResponseFormatTextJSONSchemaConfig"] + + +class BetaResponseFormatTextJSONSchemaConfig(BaseModel): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs). + """ + + name: str + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + schema_: Dict[str, object] = FieldInfo(alias="schema") + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Literal["json_schema"] + """The type of response format being defined. Always `json_schema`.""" + + description: Optional[str] = None + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + strict: Optional[bool] = None + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + """ diff --git a/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py b/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py new file mode 100644 index 0000000000..7727bf1238 --- /dev/null +++ b/src/openai/types/beta/beta_response_format_text_json_schema_config_param.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseFormatTextJSONSchemaConfigParam"] + + +class BetaResponseFormatTextJSONSchemaConfigParam(TypedDict, total=False): + """JSON Schema response format. + + Used to generate structured JSON responses. + Learn more about [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs). + """ + + name: Required[str] + """The name of the response format. + + Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length + of 64. + """ + + schema: Required[Dict[str, object]] + """ + The schema for the response format, described as a JSON Schema object. Learn how + to build JSON schemas [here](https://json-schema.org/). + """ + + type: Required[Literal["json_schema"]] + """The type of response format being defined. Always `json_schema`.""" + + description: str + """ + A description of what the response format is for, used by the model to determine + how to respond in the format. + """ + + strict: Optional[bool] + """ + Whether to enable strict schema adherence when generating the output. If set to + true, the model will always follow the exact schema defined in the `schema` + field. Only a subset of JSON Schema is supported when `strict` is `true`. To + learn more, read the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + """ diff --git a/src/openai/types/beta/beta_response_function_call_arguments_delta_event.py b/src/openai/types/beta/beta_response_function_call_arguments_delta_event.py new file mode 100644 index 0000000000..824b40a8cc --- /dev/null +++ b/src/openai/types/beta/beta_response_function_call_arguments_delta_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionCallArgumentsDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionCallArgumentsDeltaEvent(BaseModel): + """Emitted when there is a partial function-call arguments delta.""" + + delta: str + """The function-call arguments delta that is added.""" + + item_id: str + """The ID of the output item that the function-call arguments delta is added to.""" + + output_index: int + """ + The index of the output item that the function-call arguments delta is added to. + """ + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.function_call_arguments.delta"] + """The type of the event. Always `response.function_call_arguments.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_function_call_arguments_done_event.py b/src/openai/types/beta/beta_response_function_call_arguments_done_event.py new file mode 100644 index 0000000000..b9ded692bb --- /dev/null +++ b/src/openai/types/beta/beta_response_function_call_arguments_done_event.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionCallArgumentsDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionCallArgumentsDoneEvent(BaseModel): + """Emitted when function-call arguments are finalized.""" + + arguments: str + """The function-call arguments.""" + + item_id: str + """The ID of the item.""" + + name: str + """The name of the function that was called.""" + + output_index: int + """The index of the output item.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.function_call_arguments.done"] + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_function_call_output_item.py b/src/openai/types/beta/beta_response_function_call_output_item.py new file mode 100644 index 0000000000..633e1cc31e --- /dev/null +++ b/src/openai/types/beta/beta_response_function_call_output_item.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_input_file_content import BetaResponseInputFileContent +from .beta_response_input_text_content import BetaResponseInputTextContent +from .beta_response_input_image_content import BetaResponseInputImageContent + +__all__ = ["BetaResponseFunctionCallOutputItem"] + +BetaResponseFunctionCallOutputItem: TypeAlias = Annotated[ + Union[BetaResponseInputTextContent, BetaResponseInputImageContent, BetaResponseInputFileContent], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/beta_response_function_call_output_item_list.py b/src/openai/types/beta/beta_response_function_call_output_item_list.py new file mode 100644 index 0000000000..f685686a41 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_call_output_item_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_response_function_call_output_item import BetaResponseFunctionCallOutputItem + +__all__ = ["BetaResponseFunctionCallOutputItemList"] + +BetaResponseFunctionCallOutputItemList: TypeAlias = List[BetaResponseFunctionCallOutputItem] diff --git a/src/openai/types/beta/beta_response_function_call_output_item_list_param.py b/src/openai/types/beta/beta_response_function_call_output_item_list_param.py new file mode 100644 index 0000000000..6eb02aeba2 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_call_output_item_list_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_content_param import BetaResponseInputFileContentParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam + +__all__ = ["BetaResponseFunctionCallOutputItemListParam", "BetaResponseFunctionCallOutputItemParam"] + +BetaResponseFunctionCallOutputItemParam: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, BetaResponseInputFileContentParam +] + +BetaResponseFunctionCallOutputItemListParam: TypeAlias = List[BetaResponseFunctionCallOutputItemParam] diff --git a/src/openai/types/beta/beta_response_function_call_output_item_param.py b/src/openai/types/beta/beta_response_function_call_output_item_param.py new file mode 100644 index 0000000000..48233bf844 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_call_output_item_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_content_param import BetaResponseInputFileContentParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam + +__all__ = ["BetaResponseFunctionCallOutputItemParam"] + +BetaResponseFunctionCallOutputItemParam: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, BetaResponseInputFileContentParam +] diff --git a/src/openai/types/beta/beta_response_function_shell_call_output_content.py b/src/openai/types/beta/beta_response_function_shell_call_output_content.py new file mode 100644 index 0000000000..563661a9e6 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_shell_call_output_content.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionShellCallOutputContent", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """The exit code returned by the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Annotated[Union[OutcomeTimeout, OutcomeExit], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionShellCallOutputContent(BaseModel): + """Captured stdout and stderr for a portion of a shell tool call output.""" + + outcome: Outcome + """The exit or timeout outcome associated with this shell call.""" + + stderr: str + """Captured stderr output for the shell call.""" + + stdout: str + """Captured stdout output for the shell call.""" diff --git a/src/openai/types/beta/beta_response_function_shell_call_output_content_param.py b/src/openai/types/beta/beta_response_function_shell_call_output_content_param.py new file mode 100644 index 0000000000..ff0d93b769 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_shell_call_output_content_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseFunctionShellCallOutputContentParam", "Outcome", "OutcomeTimeout", "OutcomeExit"] + + +class OutcomeTimeout(TypedDict, total=False): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Required[Literal["timeout"]] + """The outcome type. Always `timeout`.""" + + +class OutcomeExit(TypedDict, total=False): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: Required[int] + """The exit code returned by the shell process.""" + + type: Required[Literal["exit"]] + """The outcome type. Always `exit`.""" + + +Outcome: TypeAlias = Union[OutcomeTimeout, OutcomeExit] + + +class BetaResponseFunctionShellCallOutputContentParam(TypedDict, total=False): + """Captured stdout and stderr for a portion of a shell tool call output.""" + + outcome: Required[Outcome] + """The exit or timeout outcome associated with this shell call.""" + + stderr: Required[str] + """Captured stderr output for the shell call.""" + + stdout: Required[str] + """Captured stdout output for the shell call.""" diff --git a/src/openai/types/beta/beta_response_function_shell_tool_call.py b/src/openai/types/beta/beta_response_function_shell_tool_call.py new file mode 100644 index 0000000000..7159936503 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_shell_tool_call.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_local_environment import BetaResponseLocalEnvironment +from .beta_response_container_reference import BetaResponseContainerReference + +__all__ = [ + "BetaResponseFunctionShellToolCall", + "Action", + "Environment", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class Action(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + + commands: List[str] + + max_output_length: Optional[int] = None + """Optional maximum number of characters to return from each command.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the commands.""" + + +Environment: TypeAlias = Annotated[ + Union[BetaResponseLocalEnvironment, BetaResponseContainerReference, None], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionShellToolCall(BaseModel): + """A tool call that executes one or more shell commands in a managed environment.""" + + id: str + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + action: Action + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + environment: Optional[Environment] = None + """Represents the use of a local environment to perform shell actions.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + type: Literal["shell_call"] + """The type of the item. Always `shell_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The ID of the entity that created this tool call.""" diff --git a/src/openai/types/beta/beta_response_function_shell_tool_call_output.py b/src/openai/types/beta/beta_response_function_shell_tool_call_output.py new file mode 100644 index 0000000000..5d2e9d8fa3 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_shell_tool_call_output.py @@ -0,0 +1,119 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseFunctionShellToolCallOutput", + "Output", + "OutputOutcome", + "OutputOutcomeTimeout", + "OutputOutcomeExit", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + + +class OutputOutcomeTimeout(BaseModel): + """Indicates that the shell call exceeded its configured time limit.""" + + type: Literal["timeout"] + """The outcome type. Always `timeout`.""" + + +class OutputOutcomeExit(BaseModel): + """Indicates that the shell commands finished and returned an exit code.""" + + exit_code: int + """Exit code from the shell process.""" + + type: Literal["exit"] + """The outcome type. Always `exit`.""" + + +OutputOutcome: TypeAlias = Annotated[Union[OutputOutcomeTimeout, OutputOutcomeExit], PropertyInfo(discriminator="type")] + + +class Output(BaseModel): + """The content of a shell tool call output that was emitted.""" + + outcome: OutputOutcome + """ + Represents either an exit outcome (with an exit code) or a timeout outcome for a + shell call output chunk. + """ + + stderr: str + """The standard error output that was captured.""" + + stdout: str + """The standard output that was captured.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionShellToolCallOutput(BaseModel): + """The output of a shell tool call that was emitted.""" + + id: str + """The unique ID of the shell call output. + + Populated when this item is returned via API. + """ + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + max_output_length: Optional[int] = None + """The maximum length of the shell command output. + + This is generated by the model and should be passed back with the raw output. + """ + + output: List[Output] + """An array of shell call output contents""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the shell call output. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + type: Literal["shell_call_output"] + """The type of the shell call output. Always `shell_call_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_function_tool_call.py b/src/openai/types/beta/beta_response_function_tool_call.py new file mode 100644 index 0000000000..9a975e0f2f --- /dev/null +++ b/src/openai/types/beta/beta_response_function_tool_call.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = ["BetaResponseFunctionToolCall", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionToolCall(BaseModel): + """A tool call to run a function. + + See the + [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information. + """ + + arguments: str + """A JSON string of the arguments to pass to the function.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + name: str + """The name of the function to run.""" + + type: Literal["function_call"] + """The type of the function tool call. Always `function_call`.""" + + id: Optional[str] = None + """The unique ID of the function tool call.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + namespace: Optional[str] = None + """The namespace of the function to run.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/beta/beta_response_function_tool_call_item.py b/src/openai/types/beta/beta_response_function_tool_call_item.py new file mode 100644 index 0000000000..af69c19a5a --- /dev/null +++ b/src/openai/types/beta/beta_response_function_tool_call_item.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from .beta_response_function_tool_call import BetaResponseFunctionToolCall + +__all__ = ["BetaResponseFunctionToolCallItem"] + + +class BetaResponseFunctionToolCallItem(BetaResponseFunctionToolCall): + """A tool call to run a function. + + See the + [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information. + """ + + id: str # type: ignore + """The unique ID of the function tool call.""" + + status: Literal["in_progress", "completed", "incomplete"] # type: ignore + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_function_tool_call_output_item.py b/src/openai/types/beta/beta_response_function_tool_call_output_item.py new file mode 100644 index 0000000000..3ed4e84964 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_tool_call_output_item.py @@ -0,0 +1,79 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = [ + "BetaResponseFunctionToolCallOutputItem", + "OutputOutputContentList", + "Agent", + "Caller", + "CallerDirect", + "CallerProgram", +] + +OutputOutputContentList: TypeAlias = Annotated[ + Union[BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + +class BetaResponseFunctionToolCallOutputItem(BaseModel): + id: str + """The unique ID of the function call tool output.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + output: Union[str, List[OutputOutputContentList]] + """ + The output from the function call generated by your code. Can be a string or an + list of output content. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_function_tool_call_param.py b/src/openai/types/beta/beta_response_function_tool_call_param.py new file mode 100644 index 0000000000..256a8bd398 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_tool_call_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["BetaResponseFunctionToolCallParam", "Agent", "Caller", "CallerDirect", "CallerProgram"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + +class BetaResponseFunctionToolCallParam(TypedDict, total=False): + """A tool call to run a function. + + See the + [function calling guide](https://platform.openai.com/docs/guides/function-calling) for more information. + """ + + arguments: Required[str] + """A JSON string of the arguments to pass to the function.""" + + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + name: Required[str] + """The name of the function to run.""" + + type: Required[Literal["function_call"]] + """The type of the function tool call. Always `function_call`.""" + + id: str + """The unique ID of the function tool call.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" + + namespace: str + """The namespace of the function to run.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/beta/beta_response_function_web_search.py b/src/openai/types/beta/beta_response_function_web_search.py new file mode 100644 index 0000000000..0d1a20e401 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_web_search.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseFunctionWebSearch", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFindInPage", + "Agent", +] + + +class ActionSearchSource(BaseModel): + """A source used in the search.""" + + type: Literal["url"] + """The type of source. Always `url`.""" + + url: str + """The URL of the source.""" + + +class ActionSearch(BaseModel): + """Action type "search" - Performs a web search query.""" + + type: Literal["search"] + """The action type.""" + + queries: Optional[List[str]] = None + """The search queries.""" + + query: Optional[str] = None + """The search query.""" + + sources: Optional[List[ActionSearchSource]] = None + """The sources used in the search.""" + + +class ActionOpenPage(BaseModel): + """Action type "open_page" - Opens a specific URL from search results.""" + + type: Literal["open_page"] + """The action type.""" + + url: Optional[str] = None + """The URL opened by the model.""" + + +class ActionFindInPage(BaseModel): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" + + pattern: str + """The pattern or text to search for within the page.""" + + type: Literal["find_in_page"] + """The action type.""" + + url: str + """The URL of the page searched for the pattern.""" + + +Action: TypeAlias = Annotated[Union[ActionSearch, ActionOpenPage, ActionFindInPage], PropertyInfo(discriminator="type")] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionWebSearch(BaseModel): + """The results of a web search tool call. + + See the + [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information. + """ + + id: str + """The unique ID of the web search tool call.""" + + action: Action + """ + An object describing the specific action taken in this web search call. Includes + details on how the model used the web (search, open_page, find_in_page). + """ + + status: Literal["in_progress", "searching", "completed", "failed"] + """The status of the web search tool call.""" + + type: Literal["web_search_call"] + """The type of the web search tool call. Always `web_search_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_function_web_search_param.py b/src/openai/types/beta/beta_response_function_web_search_param.py new file mode 100644 index 0000000000..2eb91c6446 --- /dev/null +++ b/src/openai/types/beta/beta_response_function_web_search_param.py @@ -0,0 +1,103 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr + +__all__ = [ + "BetaResponseFunctionWebSearchParam", + "Action", + "ActionSearch", + "ActionSearchSource", + "ActionOpenPage", + "ActionFindInPage", + "Agent", +] + + +class ActionSearchSource(TypedDict, total=False): + """A source used in the search.""" + + type: Required[Literal["url"]] + """The type of source. Always `url`.""" + + url: Required[str] + """The URL of the source.""" + + +class ActionSearch(TypedDict, total=False): + """Action type "search" - Performs a web search query.""" + + type: Required[Literal["search"]] + """The action type.""" + + queries: SequenceNotStr[str] + """The search queries.""" + + query: str + """The search query.""" + + sources: Iterable[ActionSearchSource] + """The sources used in the search.""" + + +class ActionOpenPage(TypedDict, total=False): + """Action type "open_page" - Opens a specific URL from search results.""" + + type: Required[Literal["open_page"]] + """The action type.""" + + url: Optional[str] + """The URL opened by the model.""" + + +class ActionFindInPage(TypedDict, total=False): + """Action type "find_in_page": Searches for a pattern within a loaded page.""" + + pattern: Required[str] + """The pattern or text to search for within the page.""" + + type: Required[Literal["find_in_page"]] + """The action type.""" + + url: Required[str] + """The URL of the page searched for the pattern.""" + + +Action: TypeAlias = Union[ActionSearch, ActionOpenPage, ActionFindInPage] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseFunctionWebSearchParam(TypedDict, total=False): + """The results of a web search tool call. + + See the + [web search guide](https://platform.openai.com/docs/guides/tools-web-search) for more information. + """ + + id: Required[str] + """The unique ID of the web search tool call.""" + + action: Required[Action] + """ + An object describing the specific action taken in this web search call. Includes + details on how the model used the web (search, open_page, find_in_page). + """ + + status: Required[Literal["in_progress", "searching", "completed", "failed"]] + """The status of the web search tool call.""" + + type: Required[Literal["web_search_call"]] + """The type of the web search tool call. Always `web_search_call`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" diff --git a/src/openai/types/beta/beta_response_image_gen_call_completed_event.py b/src/openai/types/beta/beta_response_image_gen_call_completed_event.py new file mode 100644 index 0000000000..2ba08cbb6b --- /dev/null +++ b/src/openai/types/beta/beta_response_image_gen_call_completed_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallCompletedEvent(BaseModel): + """ + Emitted when an image generation tool call has completed and the final image is available. + """ + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.image_generation_call.completed"] + """The type of the event. Always 'response.image_generation_call.completed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_image_gen_call_generating_event.py b/src/openai/types/beta/beta_response_image_gen_call_generating_event.py new file mode 100644 index 0000000000..736823afed --- /dev/null +++ b/src/openai/types/beta/beta_response_image_gen_call_generating_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallGeneratingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallGeneratingEvent(BaseModel): + """ + Emitted when an image generation tool call is actively generating an image (intermediate state). + """ + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.generating"] + """The type of the event. Always 'response.image_generation_call.generating'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py b/src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py new file mode 100644 index 0000000000..d4f896978c --- /dev/null +++ b/src/openai/types/beta/beta_response_image_gen_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallInProgressEvent(BaseModel): + """Emitted when an image generation tool call is in progress.""" + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.in_progress"] + """The type of the event. Always 'response.image_generation_call.in_progress'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py b/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py new file mode 100644 index 0000000000..f1e1509855 --- /dev/null +++ b/src/openai/types/beta/beta_response_image_gen_call_partial_image_event.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseImageGenCallPartialImageEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseImageGenCallPartialImageEvent(BaseModel): + """Emitted when a partial image is available during image generation streaming.""" + + item_id: str + """The unique identifier of the image generation item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + partial_image_b64: str + """Base64-encoded partial image data, suitable for rendering as an image.""" + + partial_image_index: int + """ + 0-based index for the partial image (backend is 1-based, but this is 0-based for + the user). + """ + + sequence_number: int + """The sequence number of the image generation item being processed.""" + + type: Literal["response.image_generation_call.partial_image"] + """The type of the event. Always 'response.image_generation_call.partial_image'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_in_progress_event.py b/src/openai/types/beta/beta_response_in_progress_event.py new file mode 100644 index 0000000000..8bedfb0c28 --- /dev/null +++ b/src/openai/types/beta/beta_response_in_progress_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseInProgressEvent(BaseModel): + """Emitted when the response is in progress.""" + + response: BetaResponse + """The response that is in progress.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.in_progress"] + """The type of the event. Always `response.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_includable.py b/src/openai/types/beta/beta_response_includable.py new file mode 100644 index 0000000000..1a2dc6a752 --- /dev/null +++ b/src/openai/types/beta/beta_response_includable.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaResponseIncludable"] + +BetaResponseIncludable: TypeAlias = Literal[ + "file_search_call.results", + "web_search_call.results", + "web_search_call.action.sources", + "message.input_image.image_url", + "computer_call_output.output.image_url", + "code_interpreter_call.outputs", + "reasoning.encrypted_content", + "message.output_text.logprobs", +] diff --git a/src/openai/types/beta/beta_response_incomplete_event.py b/src/openai/types/beta/beta_response_incomplete_event.py new file mode 100644 index 0000000000..c522b98cf1 --- /dev/null +++ b/src/openai/types/beta/beta_response_incomplete_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseIncompleteEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseIncompleteEvent(BaseModel): + """An event that is emitted when a response finishes as incomplete.""" + + response: BetaResponse + """The response that was incomplete.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.incomplete"] + """The type of the event. Always `response.incomplete`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_inject_created_event.py b/src/openai/types/beta/beta_response_inject_created_event.py new file mode 100644 index 0000000000..338df3fc6d --- /dev/null +++ b/src/openai/types/beta/beta_response_inject_created_event.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInjectCreatedEvent"] + + +class BetaResponseInjectCreatedEvent(BaseModel): + """ + Emitted when all injected input items were validated and committed to the + active response. + """ + + response_id: str + """The ID of the response that accepted the input.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.inject.created"] + """The event discriminator. Always `response.inject.created`.""" + + stream_id: Optional[str] = None + """The multiplexed WebSocket stream that emitted the event. + + This field is present only when WebSocket multiplexing is enabled separately. + """ diff --git a/src/openai/types/beta/beta_response_inject_event.py b/src/openai/types/beta/beta_response_inject_event.py new file mode 100644 index 0000000000..3b990f51db --- /dev/null +++ b/src/openai/types/beta/beta_response_inject_event.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_item import BetaResponseInputItem + +__all__ = ["BetaResponseInjectEvent"] + + +class BetaResponseInjectEvent(BaseModel): + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + + input: List[BetaResponseInputItem] + """Input items to inject into the active response.""" + + response_id: str + """The ID of the active response that should receive the input.""" + + type: Literal["response.inject"] + """The event discriminator. Always `response.inject`.""" diff --git a/src/openai/types/beta/beta_response_inject_event_param.py b/src/openai/types/beta/beta_response_inject_event_param.py new file mode 100644 index 0000000000..6f0806c6e2 --- /dev/null +++ b/src/openai/types/beta/beta_response_inject_event_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .beta_response_input_item_param import BetaResponseInputItemParam + +__all__ = ["BetaResponseInjectEventParam"] + + +class BetaResponseInjectEventParam(TypedDict, total=False): + """ + Injects input items into an active response over a WebSocket connection. + The items are validated and committed atomically. Currently, the server + accepts client-owned tool outputs that resume a waiting agent. + """ + + input: Required[Iterable[BetaResponseInputItemParam]] + """Input items to inject into the active response.""" + + response_id: Required[str] + """The ID of the active response that should receive the input.""" + + type: Required[Literal["response.inject"]] + """The event discriminator. Always `response.inject`.""" diff --git a/src/openai/types/beta/beta_response_inject_failed_event.py b/src/openai/types/beta/beta_response_inject_failed_event.py new file mode 100644 index 0000000000..a7618a2c84 --- /dev/null +++ b/src/openai/types/beta/beta_response_inject_failed_event.py @@ -0,0 +1,49 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_item import BetaResponseInputItem + +__all__ = ["BetaResponseInjectFailedEvent", "Error"] + + +class Error(BaseModel): + """Information about why the input was not committed.""" + + code: Literal["response_already_completed", "response_not_found"] + """A machine-readable error code.""" + + message: str + """A human-readable description of the error.""" + + +class BetaResponseInjectFailedEvent(BaseModel): + """Emitted when injected input could not be committed to a response. + + The event + returns the uncommitted raw input so the client can retry it in another + response when appropriate. + """ + + error: Error + """Information about why the input was not committed.""" + + input: List[BetaResponseInputItem] + """The raw input items that were not committed.""" + + response_id: str + """The ID of the response that rejected the input.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.inject.failed"] + """The event discriminator. Always `response.inject.failed`.""" + + stream_id: Optional[str] = None + """The multiplexed WebSocket stream that emitted the event. + + This field is present only when WebSocket multiplexing is enabled separately. + """ diff --git a/src/openai/types/beta/beta_response_input.py b/src/openai/types/beta/beta_response_input.py new file mode 100644 index 0000000000..acc5f5cb19 --- /dev/null +++ b/src/openai/types/beta/beta_response_input.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_response_input_item import BetaResponseInputItem + +__all__ = ["BetaResponseInput"] + +BetaResponseInput: TypeAlias = List[BetaResponseInputItem] diff --git a/src/openai/types/beta/beta_response_input_content.py b/src/openai/types/beta/beta_response_input_content.py new file mode 100644 index 0000000000..9ba31e9d64 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_content.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = ["BetaResponseInputContent"] + +BetaResponseInputContent: TypeAlias = Annotated[ + Union[BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/beta_response_input_content_param.py b/src/openai/types/beta/beta_response_input_content_param.py new file mode 100644 index 0000000000..6721cd6a05 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_content_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = ["BetaResponseInputContentParam"] + +BetaResponseInputContentParam: TypeAlias = Union[ + BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam +] diff --git a/src/openai/types/beta/beta_response_input_file.py b/src/openai/types/beta/beta_response_input_file.py new file mode 100644 index 0000000000..eb38aade52 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_file.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputFile", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFile(BaseModel): + """A file input to the model.""" + + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] = None + """The content of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_file_content.py b/src/openai/types/beta/beta_response_input_file_content.py new file mode 100644 index 0000000000..fc22bb1492 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_file_content.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputFileContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFileContent(BaseModel): + """A file input to the model.""" + + type: Literal["input_file"] + """The type of the input item. Always `input_file`.""" + + detail: Optional[Literal["auto", "low", "high"]] = None + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] = None + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] = None + """The URL of the file to be sent to the model.""" + + filename: Optional[str] = None + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_file_content_param.py b/src/openai/types/beta/beta_response_input_file_content_param.py new file mode 100644 index 0000000000..6456405d79 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_file_content_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputFileContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFileContentParam(TypedDict, total=False): + """A file input to the model.""" + + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + detail: Literal["auto", "low", "high"] + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: Optional[str] + """The base64-encoded data of the file to be sent to the model.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + file_url: Optional[str] + """The URL of the file to be sent to the model.""" + + filename: Optional[str] + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_file_param.py b/src/openai/types/beta/beta_response_input_file_param.py new file mode 100644 index 0000000000..ecbab5d915 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_file_param.py @@ -0,0 +1,53 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputFileParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputFileParam(TypedDict, total=False): + """A file input to the model.""" + + type: Required[Literal["input_file"]] + """The type of the input item. Always `input_file`.""" + + detail: Literal["auto", "low", "high"] + """The detail level of the file to be sent to the model. + + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. + """ + + file_data: str + """The content of the file to be sent to the model.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + file_url: str + """The URL of the file to be sent to the model.""" + + filename: str + """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_image.py b/src/openai/types/beta/beta_response_input_image.py new file mode 100644 index 0000000000..75669130f0 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_image.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputImage", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImage(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision). + """ + + detail: Literal["low", "high", "auto", "original"] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_image_content.py b/src/openai/types/beta/beta_response_input_image_content.py new file mode 100644 index 0000000000..31296f620d --- /dev/null +++ b/src/openai/types/beta/beta_response_input_image_content.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputImageContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImageContent(BaseModel): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision) + """ + + type: Literal["input_image"] + """The type of the input item. Always `input_image`.""" + + detail: Optional[Literal["low", "high", "auto", "original"]] = None + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] = None + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_image_content_param.py b/src/openai/types/beta/beta_response_input_image_content_param.py new file mode 100644 index 0000000000..d811d39386 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_image_content_param.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputImageContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImageContentParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision) + """ + + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + detail: Optional[Literal["low", "high", "auto", "original"]] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_image_param.py b/src/openai/types/beta/beta_response_input_image_param.py new file mode 100644 index 0000000000..1060b3c984 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_image_param.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputImageParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputImageParam(TypedDict, total=False): + """An image input to the model. + + Learn about [image inputs](https://platform.openai.com/docs/guides/vision). + """ + + detail: Required[Literal["low", "high", "auto", "original"]] + """The detail level of the image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + type: Required[Literal["input_image"]] + """The type of the input item. Always `input_image`.""" + + file_id: Optional[str] + """The ID of the file to be sent to the model.""" + + image_url: Optional[str] + """The URL of the image to be sent to the model. + + A fully qualified URL or base64 encoded image in a data URL. + """ + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py new file mode 100644 index 0000000000..d493bd040a --- /dev/null +++ b/src/openai/types/beta/beta_response_input_item.py @@ -0,0 +1,1160 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_local_environment import BetaLocalEnvironment +from .beta_easy_input_message import BetaEasyInputMessage +from .beta_container_reference import BetaContainerReference +from .beta_response_output_message import BetaResponseOutputMessage +from .beta_response_reasoning_item import BetaResponseReasoningItem +from .beta_response_custom_tool_call import BetaResponseCustomToolCall +from .beta_response_computer_tool_call import BetaResponseComputerToolCall +from .beta_response_function_tool_call import BetaResponseFunctionToolCall +from .beta_response_input_text_content import BetaResponseInputTextContent +from .beta_response_function_web_search import BetaResponseFunctionWebSearch +from .beta_response_input_image_content import BetaResponseInputImageContent +from .beta_response_compaction_item_param import BetaResponseCompactionItemParam +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall +from .beta_response_custom_tool_call_output import BetaResponseCustomToolCallOutput +from .beta_response_code_interpreter_tool_call import BetaResponseCodeInterpreterToolCall +from .beta_response_input_message_content_list import BetaResponseInputMessageContentList +from .beta_response_tool_search_output_item_param import BetaResponseToolSearchOutputItemParam +from .beta_response_function_call_output_item_list import BetaResponseFunctionCallOutputItemList +from .beta_response_function_shell_call_output_content import BetaResponseFunctionShellCallOutputContent +from .beta_response_computer_tool_call_output_screenshot import BetaResponseComputerToolCallOutputScreenshot + +__all__ = [ + "BetaResponseInputItem", + "Message", + "MessageAgent", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "ComputerCallOutputAgent", + "FunctionCallOutput", + "FunctionCallOutputAgent", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputOutput", + "MultiAgentCallOutputOutputAnnotationsUnionMember0", + "MultiAgentCallOutputOutputAnnotationsUnionMember1", + "MultiAgentCallOutputOutputAnnotationsUnionMember2", + "MultiAgentCallOutputAgent", + "ToolSearchCall", + "ToolSearchCallAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "ShellCall", + "ShellCallAction", + "ShellCallAgent", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputAgent", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallAgent", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputAgent", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", + "CompactionTrigger", + "CompactionTriggerAgent", + "ItemReference", + "ItemReferenceAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", +] + + +class MessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Message(BaseModel): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: BetaResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + agent: Optional[MessageAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Optional[Literal["message"]] = None + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(BaseModel): + """A pending safety check for the computer call.""" + + id: str + """The ID of the pending safety check.""" + + code: Optional[str] = None + """The type of the pending safety check.""" + + message: Optional[str] = None + """Details about the pending safety check.""" + + +class ComputerCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ComputerCallOutput(BaseModel): + """The output of a computer tool call.""" + + call_id: str + """The ID of the computer tool call that produced the output.""" + + output: BetaResponseComputerToolCallOutputScreenshot + """A computer screenshot image used with the computer use tool.""" + + type: Literal["computer_call_output"] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] = None + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[List[ComputerCallOutputAcknowledgedSafetyCheck]] = None + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[ComputerCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class FunctionCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Annotated[ + Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class FunctionCallOutput(BaseModel): + """The output of a function tool call.""" + + call_id: str + """The unique ID of the function tool call generated by the model.""" + + output: Union[str, BetaResponseFunctionCallOutputItemList] + """Text, image, or file output of the function tool call.""" + + type: Literal["function_call_output"] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] = None + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[FunctionCallOutputAgent] = None + """The agent that produced this item.""" + + caller: Optional[FunctionCallOutputCaller] = None + """The execution context that produced this tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class AgentMessageContentEncryptedContent(BaseModel): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: str + """Opaque encrypted content.""" + + type: Literal["encrypted_content"] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Annotated[ + Union[BetaResponseInputTextContent, BetaResponseInputImageContent, AgentMessageContentEncryptedContent], + PropertyInfo(discriminator="type"), +] + + +class AgentMessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(BaseModel): + """A message routed between agents.""" + + author: str + """The sending agent identity.""" + + content: List[AgentMessageContent] + """Plaintext, image, or encrypted content sent between agents.""" + + recipient: str + """The destination agent identity.""" + + type: Literal["agent_message"] + """The item type. Always `agent_message`.""" + + id: Optional[str] = None + """The unique ID of this agent message item.""" + + agent: Optional[AgentMessageAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(BaseModel): + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that was executed.""" + + arguments: str + """The action arguments as a JSON string.""" + + call_id: str + """The unique ID linking this call to its output.""" + + type: Literal["multi_agent_call"] + """The item type. Always `multi_agent_call`.""" + + id: Optional[str] = None + """The unique ID of this multi-agent call.""" + + agent: Optional[MultiAgentCallAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember0(BaseModel): + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The citation type. Always `file_citation`.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember1(BaseModel): + end_index: int + """The index of the last character of the citation in the message.""" + + start_index: int + """The index of the first character of the citation in the message.""" + + title: str + """The title of the cited resource.""" + + type: Literal["url_citation"] + """The citation type. Always `url_citation`.""" + + url: str + """The URL of the cited resource.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember2(BaseModel): + container_id: str + """The ID of the container.""" + + end_index: int + """The index of the last character of the citation in the message.""" + + file_id: str + """The ID of the container file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the citation in the message.""" + + type: Literal["container_file_citation"] + """The citation type. Always `container_file_citation`.""" + + +class MultiAgentCallOutputOutput(BaseModel): + text: str + """The text content.""" + + type: Literal["output_text"] + """The content type. Always `output_text`.""" + + annotations: Union[ + List[MultiAgentCallOutputOutputAnnotationsUnionMember0], + List[MultiAgentCallOutputOutputAnnotationsUnionMember1], + List[MultiAgentCallOutputOutputAnnotationsUnionMember2], + None, + ] = None + """Citations associated with the text content.""" + + +class MultiAgentCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(BaseModel): + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that produced this result.""" + + call_id: str + """The unique ID of the multi-agent call.""" + + output: List[MultiAgentCallOutputOutput] + """Text output returned by the multi-agent action.""" + + type: Literal["multi_agent_call_output"] + """The item type. Always `multi_agent_call_output`.""" + + id: Optional[str] = None + """The unique ID of this multi-agent call output.""" + + agent: Optional[MultiAgentCallOutputAgent] = None + """The agent that produced this item.""" + + +class ToolSearchCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ToolSearchCall(BaseModel): + arguments: object + """The arguments supplied to the tool search call.""" + + type: Literal["tool_search_call"] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] = None + """The unique ID of this tool search call.""" + + agent: Optional[ToolSearchCallAgent] = None + """The agent that produced this item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search call.""" + + +class AdditionalToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(BaseModel): + role: Literal["developer"] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: List[BetaTool] + """A list of additional tools made available at this item.""" + + type: Literal["additional_tools"] + """The item type. Always `additional_tools`.""" + + id: Optional[str] = None + """The unique ID of this additional tools item.""" + + agent: Optional[AdditionalToolsAgent] = None + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + agent: Optional[ImageGenerationCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(BaseModel): + """The shell commands and limits that describe how to run the tool call.""" + + commands: List[str] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] = None + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] = None + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ShellCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Annotated[ + Union[ShellCallCallerDirect, ShellCallCallerProgram, None], PropertyInfo(discriminator="type") +] + +ShellCallEnvironment: TypeAlias = Annotated[ + Union[BetaLocalEnvironment, BetaContainerReference, None], PropertyInfo(discriminator="type") +] + + +class ShellCall(BaseModel): + """A tool representing a request to execute one or more shell commands.""" + + action: ShellCallAction + """The shell commands and limits that describe how to run the tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + type: Literal["shell_call"] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] = None + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallAgent] = None + """The agent that produced this item.""" + + caller: Optional[ShellCallCaller] = None + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] = None + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ShellCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Annotated[ + Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ShellCallOutput(BaseModel): + """The streamed output items emitted by a shell tool call.""" + + call_id: str + """The unique ID of the shell tool call generated by the model.""" + + output: List[BetaResponseFunctionShellCallOutputContent] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Literal["shell_call_output"] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] = None + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallOutputAgent] = None + """The agent that produced this item.""" + + caller: Optional[ShellCallOutputCaller] = None + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] = None + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(BaseModel): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: str + """Unified diff content to apply when creating the file.""" + + path: str + """Path of the file to create relative to the workspace root.""" + + type: Literal["create_file"] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(BaseModel): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: str + """Path of the file to delete relative to the workspace root.""" + + type: Literal["delete_file"] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(BaseModel): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: str + """Unified diff content to apply to the existing file.""" + + path: str + """Path of the file to update relative to the workspace root.""" + + type: Literal["update_file"] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Annotated[ + Union[ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile], + PropertyInfo(discriminator="type"), +] + + +class ApplyPatchCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ApplyPatchCall(BaseModel): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + operation: ApplyPatchCallOperation + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Literal["in_progress", "completed"] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Literal["apply_patch_call"] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallAgent] = None + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallCaller] = None + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + +class ApplyPatchCallOutput(BaseModel): + """The streamed output emitted by an apply patch tool call.""" + + call_id: str + """The unique ID of the apply patch tool call generated by the model.""" + + status: Literal["completed", "failed"] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Literal["apply_patch_call_output"] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] = None + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallOutputAgent] = None + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallOutputCaller] = None + """The execution context that produced this tool call.""" + + output: Optional[str] = None + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] = None + """The agent that produced this item.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] = None + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] = None + """The unique ID of the approval response""" + + agent: Optional[McpApprovalResponseAgent] = None + """The agent that produced this item.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] = None + """The agent that produced this item.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[str] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTriggerAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class CompactionTrigger(BaseModel): + """Compacts the current context. Must be the final input item.""" + + type: Literal["compaction_trigger"] + """The type of the item. Always `compaction_trigger`.""" + + agent: Optional[CompactionTriggerAgent] = None + """The agent that produced this item.""" + + +class ItemReferenceAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ItemReference(BaseModel): + """An internal identifier for an item to reference.""" + + id: str + """The ID of the item to reference.""" + + agent: Optional[ItemReferenceAgent] = None + """The agent that produced this item.""" + + type: Optional[Literal["item_reference"]] = None + """The type of item to reference. Always `item_reference`.""" + + +class ProgramAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Program(BaseModel): + id: str + """The unique ID of this program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The item type. Always `program`.""" + + agent: Optional[ProgramAgent] = None + """The agent that produced this item.""" + + +class ProgramOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of this program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output.""" + + type: Literal["program_output"] + """The item type. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] = None + """The agent that produced this item.""" + + +BetaResponseInputItem: TypeAlias = Annotated[ + Union[ + BetaEasyInputMessage, + Message, + BetaResponseOutputMessage, + BetaResponseFileSearchToolCall, + BetaResponseComputerToolCall, + ComputerCallOutput, + BetaResponseFunctionWebSearch, + BetaResponseFunctionToolCall, + FunctionCallOutput, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + ToolSearchCall, + BetaResponseToolSearchOutputItemParam, + AdditionalTools, + BetaResponseReasoningItem, + BetaResponseCompactionItemParam, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallOutput, + BetaResponseCustomToolCall, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py new file mode 100644 index 0000000000..c41c57d860 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_item_param.py @@ -0,0 +1,1147 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_tool_param import BetaToolParam +from .beta_local_environment_param import BetaLocalEnvironmentParam +from .beta_easy_input_message_param import BetaEasyInputMessageParam +from .beta_container_reference_param import BetaContainerReferenceParam +from .beta_response_output_message_param import BetaResponseOutputMessageParam +from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam +from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam +from .beta_response_computer_tool_call_param import BetaResponseComputerToolCallParam +from .beta_response_function_tool_call_param import BetaResponseFunctionToolCallParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_function_web_search_param import BetaResponseFunctionWebSearchParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam +from .beta_response_compaction_item_param_param import BetaResponseCompactionItemParamParam +from .beta_response_file_search_tool_call_param import BetaResponseFileSearchToolCallParam +from .beta_response_custom_tool_call_output_param import BetaResponseCustomToolCallOutputParam +from .beta_response_code_interpreter_tool_call_param import BetaResponseCodeInterpreterToolCallParam +from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam +from .beta_response_tool_search_output_item_param_param import BetaResponseToolSearchOutputItemParamParam +from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam +from .beta_response_function_shell_call_output_content_param import BetaResponseFunctionShellCallOutputContentParam +from .beta_response_computer_tool_call_output_screenshot_param import BetaResponseComputerToolCallOutputScreenshotParam + +__all__ = [ + "BetaResponseInputItemParam", + "Message", + "MessageAgent", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "ComputerCallOutputAgent", + "FunctionCallOutput", + "FunctionCallOutputAgent", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputOutput", + "MultiAgentCallOutputOutputAnnotationsUnionMember0", + "MultiAgentCallOutputOutputAnnotationsUnionMember1", + "MultiAgentCallOutputOutputAnnotationsUnionMember2", + "MultiAgentCallOutputAgent", + "ToolSearchCall", + "ToolSearchCallAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "ShellCall", + "ShellCallAction", + "ShellCallAgent", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputAgent", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallAgent", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputAgent", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", + "CompactionTrigger", + "CompactionTriggerAgent", + "ItemReference", + "ItemReferenceAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", +] + + +class MessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: Required[BetaResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + agent: Optional[MessageAgent] + """The agent that produced this item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class ComputerCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[BetaResponseComputerToolCallOutputScreenshotParam] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[ComputerCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + +class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[FunctionCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class AgentMessageContentEncryptedContent(TypedDict, total=False): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: Required[str] + """Opaque encrypted content.""" + + type: Required[Literal["encrypted_content"]] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, AgentMessageContentEncryptedContent +] + + +class AgentMessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(TypedDict, total=False): + """A message routed between agents.""" + + author: Required[str] + """The sending agent identity.""" + + content: Required[Iterable[AgentMessageContent]] + """Plaintext, image, or encrypted content sent between agents.""" + + recipient: Required[str] + """The destination agent identity.""" + + type: Required[Literal["agent_message"]] + """The item type. Always `agent_message`.""" + + id: Optional[str] + """The unique ID of this agent message item.""" + + agent: Optional[AgentMessageAgent] + """The agent that produced this item.""" + + +class MultiAgentCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that was executed.""" + + arguments: Required[str] + """The action arguments as a JSON string.""" + + call_id: Required[str] + """The unique ID linking this call to its output.""" + + type: Required[Literal["multi_agent_call"]] + """The item type. Always `multi_agent_call`.""" + + id: Optional[str] + """The unique ID of this multi-agent call.""" + + agent: Optional[MultiAgentCallAgent] + """The agent that produced this item.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember0(TypedDict, total=False): + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the file cited.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The citation type. Always `file_citation`.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember1(TypedDict, total=False): + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + title: Required[str] + """The title of the cited resource.""" + + type: Required[Literal["url_citation"]] + """The citation type. Always `url_citation`.""" + + url: Required[str] + """The URL of the cited resource.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember2(TypedDict, total=False): + container_id: Required[str] + """The ID of the container.""" + + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + file_id: Required[str] + """The ID of the container file.""" + + filename: Required[str] + """The filename of the container file cited.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + type: Required[Literal["container_file_citation"]] + """The citation type. Always `container_file_citation`.""" + + +class MultiAgentCallOutputOutput(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["output_text"]] + """The content type. Always `output_text`.""" + + annotations: Union[ + Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember0], + Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember1], + Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember2], + ] + """Citations associated with the text content.""" + + +class MultiAgentCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that produced this result.""" + + call_id: Required[str] + """The unique ID of the multi-agent call.""" + + output: Required[Iterable[MultiAgentCallOutputOutput]] + """Text output returned by the multi-agent action.""" + + type: Required[Literal["multi_agent_call_output"]] + """The item type. Always `multi_agent_call_output`.""" + + id: Optional[str] + """The unique ID of this multi-agent call output.""" + + agent: Optional[MultiAgentCallOutputAgent] + """The agent that produced this item.""" + + +class ToolSearchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + agent: Optional[ToolSearchCallAgent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + +class AdditionalToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[BetaToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + agent: Optional[AdditionalToolsAgent] + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + + id: Required[str] + """The unique ID of the image generation call.""" + + result: Required[Optional[str]] + """The generated image encoded in base64.""" + + status: Required[Literal["in_progress", "completed", "generating", "failed"]] + """The status of the image generation call.""" + + type: Required[Literal["image_generation_call"]] + """The type of the image generation call. Always `image_generation_call`.""" + + agent: Optional[ImageGenerationCallAgent] + """The agent that produced this item.""" + + +class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + + command: Required[SequenceNotStr[str]] + """The command to run.""" + + env: Required[Dict[str, str]] + """Environment variables to set for the command.""" + + type: Required[Literal["exec"]] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] + """Optional user to run the command as.""" + + working_directory: Optional[str] + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + + id: Required[str] + """The unique ID of the local shell call.""" + + action: Required[LocalShellCallAction] + """Execute a shell command on the server.""" + + call_id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the local shell call.""" + + type: Required[Literal["local_shell_call"]] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + + id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the local shell tool call.""" + + type: Required[Literal["local_shell_call_output"]] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + +ShellCallEnvironment: TypeAlias = Union[BetaLocalEnvironmentParam, BetaContainerReferenceParam] + + +class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + +class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + output: Required[Iterable[BetaResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + +class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + + +class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + + output: Optional[str] + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class McpListToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + + id: Required[str] + """The unique ID of the list.""" + + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[McpListToolsTool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] + """The agent that produced this item.""" + + error: Optional[str] + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] + """The unique ID of the approval response""" + + agent: Optional[McpApprovalResponseAgent] + """The agent that produced this item.""" + + reason: Optional[str] + """Optional reason for the decision.""" + + +class McpCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] + """The agent that produced this item.""" + + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[str] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" + + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTriggerAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + agent: Optional[CompactionTriggerAgent] + """The agent that produced this item.""" + + +class ItemReferenceAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + + id: Required[str] + """The ID of the item to reference.""" + + agent: Optional[ItemReferenceAgent] + """The agent that produced this item.""" + + type: Optional[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +class ProgramAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + agent: Optional[ProgramAgent] + """The agent that produced this item.""" + + +class ProgramOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] + """The agent that produced this item.""" + + +BetaResponseInputItemParam: TypeAlias = Union[ + BetaEasyInputMessageParam, + Message, + BetaResponseOutputMessageParam, + BetaResponseFileSearchToolCallParam, + BetaResponseComputerToolCallParam, + ComputerCallOutput, + BetaResponseFunctionWebSearchParam, + BetaResponseFunctionToolCallParam, + FunctionCallOutput, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + ToolSearchCall, + BetaResponseToolSearchOutputItemParamParam, + AdditionalTools, + BetaResponseReasoningItemParam, + BetaResponseCompactionItemParamParam, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCallParam, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallOutputParam, + BetaResponseCustomToolCallParam, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, +] diff --git a/src/openai/types/beta/beta_response_input_message_content_list.py b/src/openai/types/beta/beta_response_input_message_content_list.py new file mode 100644 index 0000000000..52f382f532 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_message_content_list.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .beta_response_input_content import BetaResponseInputContent + +__all__ = ["BetaResponseInputMessageContentList"] + +BetaResponseInputMessageContentList: TypeAlias = List[BetaResponseInputContent] diff --git a/src/openai/types/beta/beta_response_input_message_content_list_param.py b/src/openai/types/beta/beta_response_input_message_content_list_param.py new file mode 100644 index 0000000000..6ff2f32187 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_message_content_list_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import TypeAlias + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = ["BetaResponseInputMessageContentListParam", "BetaResponseInputContentParam"] + +BetaResponseInputContentParam: TypeAlias = Union[ + BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam +] + +BetaResponseInputMessageContentListParam: TypeAlias = List[BetaResponseInputContentParam] diff --git a/src/openai/types/beta/beta_response_input_message_item.py b/src/openai/types/beta/beta_response_input_message_item.py new file mode 100644 index 0000000000..b4f58c6a0e --- /dev/null +++ b/src/openai/types/beta/beta_response_input_message_item.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_input_message_content_list import BetaResponseInputMessageContentList + +__all__ = ["BetaResponseInputMessageItem", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseInputMessageItem(BaseModel): + id: str + """The unique ID of the message input.""" + + content: BetaResponseInputMessageContentList + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Literal["user", "system", "developer"] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py new file mode 100644 index 0000000000..4b8ed10f54 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_param.py @@ -0,0 +1,1150 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_tool_param import BetaToolParam +from .beta_local_environment_param import BetaLocalEnvironmentParam +from .beta_easy_input_message_param import BetaEasyInputMessageParam +from .beta_container_reference_param import BetaContainerReferenceParam +from .beta_response_output_message_param import BetaResponseOutputMessageParam +from .beta_response_reasoning_item_param import BetaResponseReasoningItemParam +from .beta_response_custom_tool_call_param import BetaResponseCustomToolCallParam +from .beta_response_computer_tool_call_param import BetaResponseComputerToolCallParam +from .beta_response_function_tool_call_param import BetaResponseFunctionToolCallParam +from .beta_response_input_text_content_param import BetaResponseInputTextContentParam +from .beta_response_function_web_search_param import BetaResponseFunctionWebSearchParam +from .beta_response_input_image_content_param import BetaResponseInputImageContentParam +from .beta_response_compaction_item_param_param import BetaResponseCompactionItemParamParam +from .beta_response_file_search_tool_call_param import BetaResponseFileSearchToolCallParam +from .beta_response_custom_tool_call_output_param import BetaResponseCustomToolCallOutputParam +from .beta_response_code_interpreter_tool_call_param import BetaResponseCodeInterpreterToolCallParam +from .beta_response_input_message_content_list_param import BetaResponseInputMessageContentListParam +from .beta_response_tool_search_output_item_param_param import BetaResponseToolSearchOutputItemParamParam +from .beta_response_function_call_output_item_list_param import BetaResponseFunctionCallOutputItemListParam +from .beta_response_function_shell_call_output_content_param import BetaResponseFunctionShellCallOutputContentParam +from .beta_response_computer_tool_call_output_screenshot_param import BetaResponseComputerToolCallOutputScreenshotParam + +__all__ = [ + "BetaResponseInputParam", + "BetaResponseInputItemParam", + "Message", + "MessageAgent", + "ComputerCallOutput", + "ComputerCallOutputAcknowledgedSafetyCheck", + "ComputerCallOutputAgent", + "FunctionCallOutput", + "FunctionCallOutputAgent", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputOutput", + "MultiAgentCallOutputOutputAnnotationsUnionMember0", + "MultiAgentCallOutputOutputAnnotationsUnionMember1", + "MultiAgentCallOutputOutputAnnotationsUnionMember2", + "MultiAgentCallOutputAgent", + "ToolSearchCall", + "ToolSearchCallAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "ShellCall", + "ShellCallAction", + "ShellCallAgent", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", + "ShellCallEnvironment", + "ShellCallOutput", + "ShellCallOutputAgent", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", + "ApplyPatchCall", + "ApplyPatchCallOperation", + "ApplyPatchCallOperationCreateFile", + "ApplyPatchCallOperationDeleteFile", + "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallAgent", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", + "ApplyPatchCallOutput", + "ApplyPatchCallOutputAgent", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", + "CompactionTrigger", + "CompactionTriggerAgent", + "ItemReference", + "ItemReferenceAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", +] + + +class MessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Message(TypedDict, total=False): + """ + A message input to the model with a role indicating instruction following + hierarchy. Instructions given with the `developer` or `system` role take + precedence over instructions given with the `user` role. + """ + + content: Required[BetaResponseInputMessageContentListParam] + """ + A list of one or many input items to the model, containing different content + types. + """ + + role: Required[Literal["user", "system", "developer"]] + """The role of the message input. One of `user`, `system`, or `developer`.""" + + agent: Optional[MessageAgent] + """The agent that produced this item.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + type: Literal["message"] + """The type of the message input. Always set to `message`.""" + + +class ComputerCallOutputAcknowledgedSafetyCheck(TypedDict, total=False): + """A pending safety check for the computer call.""" + + id: Required[str] + """The ID of the pending safety check.""" + + code: Optional[str] + """The type of the pending safety check.""" + + message: Optional[str] + """Details about the pending safety check.""" + + +class ComputerCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ComputerCallOutput(TypedDict, total=False): + """The output of a computer tool call.""" + + call_id: Required[str] + """The ID of the computer tool call that produced the output.""" + + output: Required[BetaResponseComputerToolCallOutputScreenshotParam] + """A computer screenshot image used with the computer use tool.""" + + type: Required[Literal["computer_call_output"]] + """The type of the computer tool call output. Always `computer_call_output`.""" + + id: Optional[str] + """The ID of the computer tool call output.""" + + acknowledged_safety_checks: Optional[Iterable[ComputerCallOutputAcknowledgedSafetyCheck]] + """ + The safety checks reported by the API that have been acknowledged by the + developer. + """ + + agent: Optional[ComputerCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + +class FunctionCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + +class FunctionCallOutput(TypedDict, total=False): + """The output of a function tool call.""" + + call_id: Required[str] + """The unique ID of the function tool call generated by the model.""" + + output: Required[Union[str, BetaResponseFunctionCallOutputItemListParam]] + """Text, image, or file output of the function tool call.""" + + type: Required[Literal["function_call_output"]] + """The type of the function tool call output. Always `function_call_output`.""" + + id: Optional[str] + """The unique ID of the function tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[FunctionCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ + + +class AgentMessageContentEncryptedContent(TypedDict, total=False): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: Required[str] + """Opaque encrypted content.""" + + type: Required[Literal["encrypted_content"]] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Union[ + BetaResponseInputTextContentParam, BetaResponseInputImageContentParam, AgentMessageContentEncryptedContent +] + + +class AgentMessageAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(TypedDict, total=False): + """A message routed between agents.""" + + author: Required[str] + """The sending agent identity.""" + + content: Required[Iterable[AgentMessageContent]] + """Plaintext, image, or encrypted content sent between agents.""" + + recipient: Required[str] + """The destination agent identity.""" + + type: Required[Literal["agent_message"]] + """The item type. Always `agent_message`.""" + + id: Optional[str] + """The unique ID of this agent message item.""" + + agent: Optional[AgentMessageAgent] + """The agent that produced this item.""" + + +class MultiAgentCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that was executed.""" + + arguments: Required[str] + """The action arguments as a JSON string.""" + + call_id: Required[str] + """The unique ID linking this call to its output.""" + + type: Required[Literal["multi_agent_call"]] + """The item type. Always `multi_agent_call`.""" + + id: Optional[str] + """The unique ID of this multi-agent call.""" + + agent: Optional[MultiAgentCallAgent] + """The agent that produced this item.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember0(TypedDict, total=False): + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the file cited.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The citation type. Always `file_citation`.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember1(TypedDict, total=False): + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + title: Required[str] + """The title of the cited resource.""" + + type: Required[Literal["url_citation"]] + """The citation type. Always `url_citation`.""" + + url: Required[str] + """The URL of the cited resource.""" + + +class MultiAgentCallOutputOutputAnnotationsUnionMember2(TypedDict, total=False): + container_id: Required[str] + """The ID of the container.""" + + end_index: Required[int] + """The index of the last character of the citation in the message.""" + + file_id: Required[str] + """The ID of the container file.""" + + filename: Required[str] + """The filename of the container file cited.""" + + start_index: Required[int] + """The index of the first character of the citation in the message.""" + + type: Required[Literal["container_file_citation"]] + """The citation type. Always `container_file_citation`.""" + + +class MultiAgentCallOutputOutput(TypedDict, total=False): + text: Required[str] + """The text content.""" + + type: Required[Literal["output_text"]] + """The content type. Always `output_text`.""" + + annotations: Union[ + Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember0], + Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember1], + Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember2], + ] + """Citations associated with the text content.""" + + +class MultiAgentCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(TypedDict, total=False): + action: Required[ + Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + ] + """The multi-agent action that produced this result.""" + + call_id: Required[str] + """The unique ID of the multi-agent call.""" + + output: Required[Iterable[MultiAgentCallOutputOutput]] + """Text output returned by the multi-agent action.""" + + type: Required[Literal["multi_agent_call_output"]] + """The item type. Always `multi_agent_call_output`.""" + + id: Optional[str] + """The unique ID of this multi-agent call output.""" + + agent: Optional[MultiAgentCallOutputAgent] + """The agent that produced this item.""" + + +class ToolSearchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ToolSearchCall(TypedDict, total=False): + arguments: Required[object] + """The arguments supplied to the tool search call.""" + + type: Required[Literal["tool_search_call"]] + """The item type. Always `tool_search_call`.""" + + id: Optional[str] + """The unique ID of this tool search call.""" + + agent: Optional[ToolSearchCallAgent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search call.""" + + +class AdditionalToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(TypedDict, total=False): + role: Required[Literal["developer"]] + """The role that provided the additional tools. Only `developer` is supported.""" + + tools: Required[Iterable[BetaToolParam]] + """A list of additional tools made available at this item.""" + + type: Required[Literal["additional_tools"]] + """The item type. Always `additional_tools`.""" + + id: Optional[str] + """The unique ID of this additional tools item.""" + + agent: Optional[AdditionalToolsAgent] + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(TypedDict, total=False): + """An image generation request made by the model.""" + + id: Required[str] + """The unique ID of the image generation call.""" + + result: Required[Optional[str]] + """The generated image encoded in base64.""" + + status: Required[Literal["in_progress", "completed", "generating", "failed"]] + """The status of the image generation call.""" + + type: Required[Literal["image_generation_call"]] + """The type of the image generation call. Always `image_generation_call`.""" + + agent: Optional[ImageGenerationCallAgent] + """The agent that produced this item.""" + + +class LocalShellCallAction(TypedDict, total=False): + """Execute a shell command on the server.""" + + command: Required[SequenceNotStr[str]] + """The command to run.""" + + env: Required[Dict[str, str]] + """Environment variables to set for the command.""" + + type: Required[Literal["exec"]] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] + """Optional user to run the command as.""" + + working_directory: Optional[str] + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(TypedDict, total=False): + """A tool call to run a command on the local shell.""" + + id: Required[str] + """The unique ID of the local shell call.""" + + action: Required[LocalShellCallAction] + """Execute a shell command on the server.""" + + call_id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the local shell call.""" + + type: Required[Literal["local_shell_call"]] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(TypedDict, total=False): + """The output of a local shell tool call.""" + + id: Required[str] + """The unique ID of the local shell tool call generated by the model.""" + + output: Required[str] + """A JSON string of the output of the local shell tool call.""" + + type: Required[Literal["local_shell_call_output"]] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class ShellCallAction(TypedDict, total=False): + """The shell commands and limits that describe how to run the tool call.""" + + commands: Required[SequenceNotStr[str]] + """Ordered shell commands for the execution environment to run.""" + + max_output_length: Optional[int] + """ + Maximum number of UTF-8 characters to capture from combined stdout and stderr + output. + """ + + timeout_ms: Optional[int] + """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" + + +class ShellCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + +ShellCallEnvironment: TypeAlias = Union[BetaLocalEnvironmentParam, BetaContainerReferenceParam] + + +class ShellCall(TypedDict, total=False): + """A tool representing a request to execute one or more shell commands.""" + + action: Required[ShellCallAction] + """The shell commands and limits that describe how to run the tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + type: Required[Literal["shell_call"]] + """The type of the item. Always `shell_call`.""" + + id: Optional[str] + """The unique ID of the shell tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + + environment: Optional[ShellCallEnvironment] + """The environment to execute the shell commands in.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call. + + One of `in_progress`, `completed`, or `incomplete`. + """ + + +class ShellCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + +class ShellCallOutput(TypedDict, total=False): + """The streamed output items emitted by a shell tool call.""" + + call_id: Required[str] + """The unique ID of the shell tool call generated by the model.""" + + output: Required[Iterable[BetaResponseFunctionShellCallOutputContentParam]] + """ + Captured chunks of stdout and stderr output, along with their associated + outcomes. + """ + + type: Required[Literal["shell_call_output"]] + """The type of the item. Always `shell_call_output`.""" + + id: Optional[str] + """The unique ID of the shell tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ShellCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + + max_output_length: Optional[int] + """ + The maximum number of UTF-8 characters captured for this shell call's combined + output. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the shell call output.""" + + +class ApplyPatchCallOperationCreateFile(TypedDict, total=False): + """Instruction for creating a new file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply when creating the file.""" + + path: Required[str] + """Path of the file to create relative to the workspace root.""" + + type: Required[Literal["create_file"]] + """The operation type. Always `create_file`.""" + + +class ApplyPatchCallOperationDeleteFile(TypedDict, total=False): + """Instruction for deleting an existing file via the apply_patch tool.""" + + path: Required[str] + """Path of the file to delete relative to the workspace root.""" + + type: Required[Literal["delete_file"]] + """The operation type. Always `delete_file`.""" + + +class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): + """Instruction for updating an existing file via the apply_patch tool.""" + + diff: Required[str] + """Unified diff content to apply to the existing file.""" + + path: Required[str] + """Path of the file to update relative to the workspace root.""" + + type: Required[Literal["update_file"]] + """The operation type. Always `update_file`.""" + + +ApplyPatchCallOperation: TypeAlias = Union[ + ApplyPatchCallOperationCreateFile, ApplyPatchCallOperationDeleteFile, ApplyPatchCallOperationUpdateFile +] + + +class ApplyPatchCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + +class ApplyPatchCall(TypedDict, total=False): + """ + A tool call representing a request to create, delete, or update files using diff patches. + """ + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + operation: Required[ApplyPatchCallOperation] + """ + The specific create, delete, or update instruction for the apply_patch tool + call. + """ + + status: Required[Literal["in_progress", "completed"]] + """The status of the apply patch tool call. One of `in_progress` or `completed`.""" + + type: Required[Literal["apply_patch_call"]] + """The type of the item. Always `apply_patch_call`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + + +class ApplyPatchCallOutput(TypedDict, total=False): + """The streamed output emitted by an apply patch tool call.""" + + call_id: Required[str] + """The unique ID of the apply patch tool call generated by the model.""" + + status: Required[Literal["completed", "failed"]] + """The status of the apply patch tool call output. One of `completed` or `failed`.""" + + type: Required[Literal["apply_patch_call_output"]] + """The type of the item. Always `apply_patch_call_output`.""" + + id: Optional[str] + """The unique ID of the apply patch tool call output. + + Populated when this item is returned via API. + """ + + agent: Optional[ApplyPatchCallOutputAgent] + """The agent that produced this item.""" + + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + + output: Optional[str] + """ + Optional human-readable log text from the apply patch tool (e.g., patch results + or errors). + """ + + +class McpListToolsTool(TypedDict, total=False): + """A tool available on an MCP server.""" + + input_schema: Required[object] + """The JSON schema describing the tool's input.""" + + name: Required[str] + """The name of the tool.""" + + annotations: Optional[object] + """Additional annotations about the tool.""" + + description: Optional[str] + """The description of the tool.""" + + +class McpListToolsAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpListTools(TypedDict, total=False): + """A list of tools available on an MCP server.""" + + id: Required[str] + """The unique ID of the list.""" + + server_label: Required[str] + """The label of the MCP server.""" + + tools: Required[Iterable[McpListToolsTool]] + """The tools available on the server.""" + + type: Required[Literal["mcp_list_tools"]] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] + """The agent that produced this item.""" + + error: Optional[str] + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(TypedDict, total=False): + """A request for human approval of a tool invocation.""" + + id: Required[str] + """The unique ID of the approval request.""" + + arguments: Required[str] + """A JSON string of arguments for the tool.""" + + name: Required[str] + """The name of the tool to run.""" + + server_label: Required[str] + """The label of the MCP server making the request.""" + + type: Required[Literal["mcp_approval_request"]] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(TypedDict, total=False): + """A response to an MCP approval request.""" + + approval_request_id: Required[str] + """The ID of the approval request being answered.""" + + approve: Required[bool] + """Whether the request was approved.""" + + type: Required[Literal["mcp_approval_response"]] + """The type of the item. Always `mcp_approval_response`.""" + + id: Optional[str] + """The unique ID of the approval response""" + + agent: Optional[McpApprovalResponseAgent] + """The agent that produced this item.""" + + reason: Optional[str] + """Optional reason for the decision.""" + + +class McpCallAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class McpCall(TypedDict, total=False): + """An invocation of a tool on an MCP server.""" + + id: Required[str] + """The unique ID of the tool call.""" + + arguments: Required[str] + """A JSON string of the arguments passed to the tool.""" + + name: Required[str] + """The name of the tool that was run.""" + + server_label: Required[str] + """The label of the MCP server running the tool.""" + + type: Required[Literal["mcp_call"]] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] + """The agent that produced this item.""" + + approval_request_id: Optional[str] + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[str] + """The error from the tool call, if any.""" + + output: Optional[str] + """The output from the tool call.""" + + status: Literal["in_progress", "completed", "incomplete", "calling", "failed"] + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class CompactionTriggerAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class CompactionTrigger(TypedDict, total=False): + """Compacts the current context. Must be the final input item.""" + + type: Required[Literal["compaction_trigger"]] + """The type of the item. Always `compaction_trigger`.""" + + agent: Optional[CompactionTriggerAgent] + """The agent that produced this item.""" + + +class ItemReferenceAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ItemReference(TypedDict, total=False): + """An internal identifier for an item to reference.""" + + id: Required[str] + """The ID of the item to reference.""" + + agent: Optional[ItemReferenceAgent] + """The agent that produced this item.""" + + type: Optional[Literal["item_reference"]] + """The type of item to reference. Always `item_reference`.""" + + +class ProgramAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + agent: Optional[ProgramAgent] + """The agent that produced this item.""" + + +class ProgramOutputAgent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] + """The agent that produced this item.""" + + +BetaResponseInputItemParam: TypeAlias = Union[ + BetaEasyInputMessageParam, + Message, + BetaResponseOutputMessageParam, + BetaResponseFileSearchToolCallParam, + BetaResponseComputerToolCallParam, + ComputerCallOutput, + BetaResponseFunctionWebSearchParam, + BetaResponseFunctionToolCallParam, + FunctionCallOutput, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + ToolSearchCall, + BetaResponseToolSearchOutputItemParamParam, + AdditionalTools, + BetaResponseReasoningItemParam, + BetaResponseCompactionItemParamParam, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCallParam, + LocalShellCall, + LocalShellCallOutput, + ShellCall, + ShellCallOutput, + ApplyPatchCall, + ApplyPatchCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallOutputParam, + BetaResponseCustomToolCallParam, + CompactionTrigger, + ItemReference, + Program, + ProgramOutput, +] + +BetaResponseInputParam: TypeAlias = List[BetaResponseInputItemParam] diff --git a/src/openai/types/beta/beta_response_input_text.py b/src/openai/types/beta/beta_response_input_text.py new file mode 100644 index 0000000000..68f158c231 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_text.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputText", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputText(BaseModel): + """A text input to the model.""" + + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_text_content.py b/src/openai/types/beta/beta_response_input_text_content.py new file mode 100644 index 0000000000..c3e4b3ede1 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_text_content.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseInputTextContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputTextContent(BaseModel): + """A text input to the model.""" + + text: str + """The text input to the model.""" + + type: Literal["input_text"] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_text_content_param.py b/src/openai/types/beta/beta_response_input_text_content_param.py new file mode 100644 index 0000000000..f00ca7d5f1 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_text_content_param.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputTextContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputTextContentParam(TypedDict, total=False): + """A text input to the model.""" + + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_input_text_param.py b/src/openai/types/beta/beta_response_input_text_param.py new file mode 100644 index 0000000000..87bc4480a9 --- /dev/null +++ b/src/openai/types/beta/beta_response_input_text_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseInputTextParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + +class BetaResponseInputTextParam(TypedDict, total=False): + """A text input to the model.""" + + text: Required[str] + """The text input to the model.""" + + type: Required[Literal["input_text"]] + """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/beta/beta_response_item.py b/src/openai/types/beta/beta_response_item.py new file mode 100644 index 0000000000..5c8c42ef11 --- /dev/null +++ b/src/openai/types/beta/beta_response_item.py @@ -0,0 +1,619 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_message import BetaResponseOutputMessage +from .beta_response_output_refusal import BetaResponseOutputRefusal +from .beta_response_reasoning_item import BetaResponseReasoningItem +from .beta_response_compaction_item import BetaResponseCompactionItem +from .beta_response_tool_search_call import BetaResponseToolSearchCall +from .beta_response_computer_tool_call import BetaResponseComputerToolCall +from .beta_response_input_message_item import BetaResponseInputMessageItem +from .beta_response_function_web_search import BetaResponseFunctionWebSearch +from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall +from .beta_response_custom_tool_call_item import BetaResponseCustomToolCallItem +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall +from .beta_response_function_tool_call_item import BetaResponseFunctionToolCallItem +from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem +from .beta_response_function_shell_tool_call import BetaResponseFunctionShellToolCall +from .beta_response_code_interpreter_tool_call import BetaResponseCodeInterpreterToolCall +from .beta_response_apply_patch_tool_call_output import BetaResponseApplyPatchToolCallOutput +from .beta_response_custom_tool_call_output_item import BetaResponseCustomToolCallOutputItem +from .beta_response_computer_tool_call_output_item import BetaResponseComputerToolCallOutputItem +from .beta_response_function_tool_call_output_item import BetaResponseFunctionToolCallOutputItem +from .beta_response_function_shell_tool_call_output import BetaResponseFunctionShellToolCallOutput + +__all__ = [ + "BetaResponseItem", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentText", + "AgentMessageContentSummaryText", + "AgentMessageContentReasoningText", + "AgentMessageContentComputerScreenshot", + "AgentMessageContentComputerScreenshotPromptCacheBreakpoint", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", + "McpCall", + "McpCallAgent", +] + + +class AgentMessageContentText(BaseModel): + """A text content.""" + + text: str + + type: Literal["text"] + + +class AgentMessageContentSummaryText(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class AgentMessageContentReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class AgentMessageContentComputerScreenshotPromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class AgentMessageContentComputerScreenshot(BaseModel): + """A screenshot of a computer.""" + + detail: Literal["low", "high", "auto", "original"] + """The detail level of the screenshot image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + prompt_cache_breakpoint: Optional[AgentMessageContentComputerScreenshotPromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ + + +class AgentMessageContentEncryptedContent(BaseModel): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: str + """Opaque encrypted content.""" + + type: Literal["encrypted_content"] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Annotated[ + Union[ + BetaResponseInputText, + BetaResponseOutputText, + AgentMessageContentText, + AgentMessageContentSummaryText, + AgentMessageContentReasoningText, + BetaResponseOutputRefusal, + BetaResponseInputImage, + AgentMessageContentComputerScreenshot, + BetaResponseInputFile, + AgentMessageContentEncryptedContent, + ], + PropertyInfo(discriminator="type"), +] + + +class AgentMessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(BaseModel): + id: str + """The unique ID of the agent message.""" + + author: str + """The sending agent identity.""" + + content: List[AgentMessageContent] + """Encrypted content sent between agents.""" + + recipient: str + """The destination agent identity.""" + + type: Literal["agent_message"] + """The type of the item. Always `agent_message`.""" + + agent: Optional[AgentMessageAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(BaseModel): + id: str + """The unique ID of the multi-agent call item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action to execute.""" + + arguments: str + """The JSON string of arguments generated for the action.""" + + call_id: str + """The unique ID linking this call to its output.""" + + type: Literal["multi_agent_call"] + """The type of the multi-agent call. Always `multi_agent_call`.""" + + agent: Optional[MultiAgentCallAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(BaseModel): + id: str + """The unique ID of the multi-agent call output item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that produced this result.""" + + call_id: str + """The unique ID of the multi-agent call.""" + + output: List[BetaResponseOutputText] + """Text output returned by the multi-agent action.""" + + type: Literal["multi_agent_call_output"] + """The type of the multi-agent result. Always `multi_agent_call_output`.""" + + agent: Optional[MultiAgentCallOutputAgent] = None + """The agent that produced this item.""" + + +class AdditionalToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[BetaTool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + agent: Optional[AdditionalToolsAgent] = None + """The agent that produced this item.""" + + +class ProgramAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + agent: Optional[ProgramAgent] = None + """The agent that produced this item.""" + + +class ProgramOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] = None + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + agent: Optional[ImageGenerationCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] = None + """The agent that produced this item.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] = None + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + agent: Optional[McpApprovalResponseAgent] = None + """The agent that produced this item.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +class McpCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] = None + """The agent that produced this item.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[str] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +BetaResponseItem: TypeAlias = Annotated[ + Union[ + BetaResponseInputMessageItem, + BetaResponseOutputMessage, + BetaResponseFileSearchToolCall, + BetaResponseComputerToolCall, + BetaResponseComputerToolCallOutputItem, + BetaResponseFunctionWebSearch, + BetaResponseFunctionToolCallItem, + BetaResponseFunctionToolCallOutputItem, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + BetaResponseToolSearchCall, + BetaResponseToolSearchOutputItem, + AdditionalTools, + BetaResponseReasoningItem, + Program, + ProgramOutput, + BetaResponseCompactionItem, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + BetaResponseFunctionShellToolCall, + BetaResponseFunctionShellToolCallOutput, + BetaResponseApplyPatchToolCall, + BetaResponseApplyPatchToolCallOutput, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + McpCall, + BetaResponseCustomToolCallItem, + BetaResponseCustomToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/beta_response_local_environment.py b/src/openai/types/beta/beta_response_local_environment.py new file mode 100644 index 0000000000..c31d123754 --- /dev/null +++ b/src/openai/types/beta/beta_response_local_environment.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseLocalEnvironment"] + + +class BetaResponseLocalEnvironment(BaseModel): + """Represents the use of a local environment to perform shell actions.""" + + type: Literal["local"] + """The environment type. Always `local`.""" diff --git a/src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py b/src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py new file mode 100644 index 0000000000..de83fc6799 --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_call_arguments_delta_event.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallArgumentsDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallArgumentsDeltaEvent(BaseModel): + """ + Emitted when there is a delta (partial update) to the arguments of an MCP tool call. + """ + + delta: str + """ + A JSON string containing the partial update to the arguments for the MCP tool + call. + """ + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call_arguments.delta"] + """The type of the event. Always 'response.mcp_call_arguments.delta'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py b/src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py new file mode 100644 index 0000000000..3cc440ed33 --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_call_arguments_done_event.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallArgumentsDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallArgumentsDoneEvent(BaseModel): + """Emitted when the arguments for an MCP tool call are finalized.""" + + arguments: str + """A JSON string containing the finalized arguments for the MCP tool call.""" + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call_arguments.done"] + """The type of the event. Always 'response.mcp_call_arguments.done'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_mcp_call_completed_event.py b/src/openai/types/beta/beta_response_mcp_call_completed_event.py new file mode 100644 index 0000000000..f7e154a182 --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_call_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallCompletedEvent(BaseModel): + """Emitted when an MCP tool call has completed successfully.""" + + item_id: str + """The ID of the MCP tool call item that completed.""" + + output_index: int + """The index of the output item that completed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.completed"] + """The type of the event. Always 'response.mcp_call.completed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_mcp_call_failed_event.py b/src/openai/types/beta/beta_response_mcp_call_failed_event.py new file mode 100644 index 0000000000..bb26a09f5b --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_call_failed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallFailedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallFailedEvent(BaseModel): + """Emitted when an MCP tool call has failed.""" + + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.failed"] + """The type of the event. Always 'response.mcp_call.failed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_mcp_call_in_progress_event.py b/src/openai/types/beta/beta_response_mcp_call_in_progress_event.py new file mode 100644 index 0000000000..abf25ab806 --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpCallInProgressEvent(BaseModel): + """Emitted when an MCP tool call is in progress.""" + + item_id: str + """The unique identifier of the MCP tool call item being processed.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_call.in_progress"] + """The type of the event. Always 'response.mcp_call.in_progress'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py b/src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py new file mode 100644 index 0000000000..c9ec208691 --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_list_tools_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpListToolsCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpListToolsCompletedEvent(BaseModel): + """Emitted when the list of available MCP tools has been successfully retrieved.""" + + item_id: str + """The ID of the MCP tool call item that produced this output.""" + + output_index: int + """The index of the output item that was processed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.completed"] + """The type of the event. Always 'response.mcp_list_tools.completed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py b/src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py new file mode 100644 index 0000000000..85acd9a613 --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_list_tools_failed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpListToolsFailedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpListToolsFailedEvent(BaseModel): + """Emitted when the attempt to list available MCP tools has failed.""" + + item_id: str + """The ID of the MCP tool call item that failed.""" + + output_index: int + """The index of the output item that failed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.failed"] + """The type of the event. Always 'response.mcp_list_tools.failed'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py b/src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py new file mode 100644 index 0000000000..36d394eefe --- /dev/null +++ b/src/openai/types/beta/beta_response_mcp_list_tools_in_progress_event.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseMcpListToolsInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseMcpListToolsInProgressEvent(BaseModel): + """ + Emitted when the system is in the process of retrieving the list of available MCP tools. + """ + + item_id: str + """The ID of the MCP tool call item that is being processed.""" + + output_index: int + """The index of the output item that is being processed.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.mcp_list_tools.in_progress"] + """The type of the event. Always 'response.mcp_list_tools.in_progress'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_output_item.py b/src/openai/types/beta/beta_response_output_item.py new file mode 100644 index 0000000000..7b6c81c02a --- /dev/null +++ b/src/openai/types/beta/beta_response_output_item.py @@ -0,0 +1,617 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_message import BetaResponseOutputMessage +from .beta_response_output_refusal import BetaResponseOutputRefusal +from .beta_response_reasoning_item import BetaResponseReasoningItem +from .beta_response_compaction_item import BetaResponseCompactionItem +from .beta_response_custom_tool_call import BetaResponseCustomToolCall +from .beta_response_tool_search_call import BetaResponseToolSearchCall +from .beta_response_computer_tool_call import BetaResponseComputerToolCall +from .beta_response_function_tool_call import BetaResponseFunctionToolCall +from .beta_response_function_web_search import BetaResponseFunctionWebSearch +from .beta_response_apply_patch_tool_call import BetaResponseApplyPatchToolCall +from .beta_response_file_search_tool_call import BetaResponseFileSearchToolCall +from .beta_response_tool_search_output_item import BetaResponseToolSearchOutputItem +from .beta_response_function_shell_tool_call import BetaResponseFunctionShellToolCall +from .beta_response_code_interpreter_tool_call import BetaResponseCodeInterpreterToolCall +from .beta_response_apply_patch_tool_call_output import BetaResponseApplyPatchToolCallOutput +from .beta_response_custom_tool_call_output_item import BetaResponseCustomToolCallOutputItem +from .beta_response_computer_tool_call_output_item import BetaResponseComputerToolCallOutputItem +from .beta_response_function_tool_call_output_item import BetaResponseFunctionToolCallOutputItem +from .beta_response_function_shell_tool_call_output import BetaResponseFunctionShellToolCallOutput + +__all__ = [ + "BetaResponseOutputItem", + "AgentMessage", + "AgentMessageContent", + "AgentMessageContentText", + "AgentMessageContentSummaryText", + "AgentMessageContentReasoningText", + "AgentMessageContentComputerScreenshot", + "AgentMessageContentComputerScreenshotPromptCacheBreakpoint", + "AgentMessageContentEncryptedContent", + "AgentMessageAgent", + "MultiAgentCall", + "MultiAgentCallAgent", + "MultiAgentCallOutput", + "MultiAgentCallOutputAgent", + "Program", + "ProgramAgent", + "ProgramOutput", + "ProgramOutputAgent", + "AdditionalTools", + "AdditionalToolsAgent", + "ImageGenerationCall", + "ImageGenerationCallAgent", + "LocalShellCall", + "LocalShellCallAction", + "LocalShellCallAgent", + "LocalShellCallOutput", + "LocalShellCallOutputAgent", + "McpCall", + "McpCallAgent", + "McpListTools", + "McpListToolsTool", + "McpListToolsAgent", + "McpApprovalRequest", + "McpApprovalRequestAgent", + "McpApprovalResponse", + "McpApprovalResponseAgent", +] + + +class AgentMessageContentText(BaseModel): + """A text content.""" + + text: str + + type: Literal["text"] + + +class AgentMessageContentSummaryText(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class AgentMessageContentReasoningText(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class AgentMessageContentComputerScreenshotPromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + +class AgentMessageContentComputerScreenshot(BaseModel): + """A screenshot of a computer.""" + + detail: Literal["low", "high", "auto", "original"] + """The detail level of the screenshot image to be sent to the model. + + One of `high`, `low`, `auto`, or `original`. Defaults to `auto`. + """ + + file_id: Optional[str] = None + """The identifier of an uploaded file that contains the screenshot.""" + + image_url: Optional[str] = None + """The URL of the screenshot image.""" + + type: Literal["computer_screenshot"] + """Specifies the event type. + + For a computer screenshot, this property is always set to `computer_screenshot`. + """ + + prompt_cache_breakpoint: Optional[AgentMessageContentComputerScreenshotPromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ + + +class AgentMessageContentEncryptedContent(BaseModel): + """ + Opaque encrypted content that Responses API decrypts inside trusted model execution. + """ + + encrypted_content: str + """Opaque encrypted content.""" + + type: Literal["encrypted_content"] + """The type of the input item. Always `encrypted_content`.""" + + +AgentMessageContent: TypeAlias = Annotated[ + Union[ + BetaResponseInputText, + BetaResponseOutputText, + AgentMessageContentText, + AgentMessageContentSummaryText, + AgentMessageContentReasoningText, + BetaResponseOutputRefusal, + BetaResponseInputImage, + AgentMessageContentComputerScreenshot, + BetaResponseInputFile, + AgentMessageContentEncryptedContent, + ], + PropertyInfo(discriminator="type"), +] + + +class AgentMessageAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AgentMessage(BaseModel): + id: str + """The unique ID of the agent message.""" + + author: str + """The sending agent identity.""" + + content: List[AgentMessageContent] + """Encrypted content sent between agents.""" + + recipient: str + """The destination agent identity.""" + + type: Literal["agent_message"] + """The type of the item. Always `agent_message`.""" + + agent: Optional[AgentMessageAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCall(BaseModel): + id: str + """The unique ID of the multi-agent call item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action to execute.""" + + arguments: str + """The JSON string of arguments generated for the action.""" + + call_id: str + """The unique ID linking this call to its output.""" + + type: Literal["multi_agent_call"] + """The type of the multi-agent call. Always `multi_agent_call`.""" + + agent: Optional[MultiAgentCallAgent] = None + """The agent that produced this item.""" + + +class MultiAgentCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class MultiAgentCallOutput(BaseModel): + id: str + """The unique ID of the multi-agent call output item.""" + + action: Literal["spawn_agent", "interrupt_agent", "list_agents", "send_message", "followup_task", "wait_agent"] + """The multi-agent action that produced this result.""" + + call_id: str + """The unique ID of the multi-agent call.""" + + output: List[BetaResponseOutputText] + """Text output returned by the multi-agent action.""" + + type: Literal["multi_agent_call_output"] + """The type of the multi-agent result. Always `multi_agent_call_output`.""" + + agent: Optional[MultiAgentCallOutputAgent] = None + """The agent that produced this item.""" + + +class ProgramAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + agent: Optional[ProgramAgent] = None + """The agent that produced this item.""" + + +class ProgramOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + agent: Optional[ProgramOutputAgent] = None + """The agent that produced this item.""" + + +class AdditionalToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class AdditionalTools(BaseModel): + id: str + """The unique ID of the additional tools item.""" + + role: Literal["unknown", "user", "assistant", "system", "critic", "discriminator", "developer", "tool"] + """The role that provided the additional tools.""" + + tools: List[BetaTool] + """The additional tool definitions made available at this item.""" + + type: Literal["additional_tools"] + """The type of the item. Always `additional_tools`.""" + + agent: Optional[AdditionalToolsAgent] = None + """The agent that produced this item.""" + + +class ImageGenerationCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class ImageGenerationCall(BaseModel): + """An image generation request made by the model.""" + + id: str + """The unique ID of the image generation call.""" + + result: Optional[str] = None + """The generated image encoded in base64.""" + + status: Literal["in_progress", "completed", "generating", "failed"] + """The status of the image generation call.""" + + type: Literal["image_generation_call"] + """The type of the image generation call. Always `image_generation_call`.""" + + agent: Optional[ImageGenerationCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallAction(BaseModel): + """Execute a shell command on the server.""" + + command: List[str] + """The command to run.""" + + env: Dict[str, str] + """Environment variables to set for the command.""" + + type: Literal["exec"] + """The type of the local shell action. Always `exec`.""" + + timeout_ms: Optional[int] = None + """Optional timeout in milliseconds for the command.""" + + user: Optional[str] = None + """Optional user to run the command as.""" + + working_directory: Optional[str] = None + """Optional working directory to run the command in.""" + + +class LocalShellCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCall(BaseModel): + """A tool call to run a command on the local shell.""" + + id: str + """The unique ID of the local shell call.""" + + action: LocalShellCallAction + """Execute a shell command on the server.""" + + call_id: str + """The unique ID of the local shell tool call generated by the model.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the local shell call.""" + + type: Literal["local_shell_call"] + """The type of the local shell call. Always `local_shell_call`.""" + + agent: Optional[LocalShellCallAgent] = None + """The agent that produced this item.""" + + +class LocalShellCallOutputAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class LocalShellCallOutput(BaseModel): + """The output of a local shell tool call.""" + + id: str + """The unique ID of the local shell tool call generated by the model.""" + + output: str + """A JSON string of the output of the local shell tool call.""" + + type: Literal["local_shell_call_output"] + """The type of the local shell tool call output. Always `local_shell_call_output`.""" + + agent: Optional[LocalShellCallOutputAgent] = None + """The agent that produced this item.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. One of `in_progress`, `completed`, or `incomplete`.""" + + +class McpCallAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpCall(BaseModel): + """An invocation of a tool on an MCP server.""" + + id: str + """The unique ID of the tool call.""" + + arguments: str + """A JSON string of the arguments passed to the tool.""" + + name: str + """The name of the tool that was run.""" + + server_label: str + """The label of the MCP server running the tool.""" + + type: Literal["mcp_call"] + """The type of the item. Always `mcp_call`.""" + + agent: Optional[McpCallAgent] = None + """The agent that produced this item.""" + + approval_request_id: Optional[str] = None + """ + Unique identifier for the MCP tool call approval request. Include this value in + a subsequent `mcp_approval_response` input to approve or reject the + corresponding tool call. + """ + + error: Optional[str] = None + """The error from the tool call, if any.""" + + output: Optional[str] = None + """The output from the tool call.""" + + status: Optional[Literal["in_progress", "completed", "incomplete", "calling", "failed"]] = None + """The status of the tool call. + + One of `in_progress`, `completed`, `incomplete`, `calling`, or `failed`. + """ + + +class McpListToolsTool(BaseModel): + """A tool available on an MCP server.""" + + input_schema: object + """The JSON schema describing the tool's input.""" + + name: str + """The name of the tool.""" + + annotations: Optional[object] = None + """Additional annotations about the tool.""" + + description: Optional[str] = None + """The description of the tool.""" + + +class McpListToolsAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpListTools(BaseModel): + """A list of tools available on an MCP server.""" + + id: str + """The unique ID of the list.""" + + server_label: str + """The label of the MCP server.""" + + tools: List[McpListToolsTool] + """The tools available on the server.""" + + type: Literal["mcp_list_tools"] + """The type of the item. Always `mcp_list_tools`.""" + + agent: Optional[McpListToolsAgent] = None + """The agent that produced this item.""" + + error: Optional[str] = None + """Error message if the server could not list tools.""" + + +class McpApprovalRequestAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalRequest(BaseModel): + """A request for human approval of a tool invocation.""" + + id: str + """The unique ID of the approval request.""" + + arguments: str + """A JSON string of arguments for the tool.""" + + name: str + """The name of the tool to run.""" + + server_label: str + """The label of the MCP server making the request.""" + + type: Literal["mcp_approval_request"] + """The type of the item. Always `mcp_approval_request`.""" + + agent: Optional[McpApprovalRequestAgent] = None + """The agent that produced this item.""" + + +class McpApprovalResponseAgent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class McpApprovalResponse(BaseModel): + """A response to an MCP approval request.""" + + id: str + """The unique ID of the approval response""" + + approval_request_id: str + """The ID of the approval request being answered.""" + + approve: bool + """Whether the request was approved.""" + + type: Literal["mcp_approval_response"] + """The type of the item. Always `mcp_approval_response`.""" + + agent: Optional[McpApprovalResponseAgent] = None + """The agent that produced this item.""" + + reason: Optional[str] = None + """Optional reason for the decision.""" + + +BetaResponseOutputItem: TypeAlias = Annotated[ + Union[ + BetaResponseOutputMessage, + BetaResponseFileSearchToolCall, + BetaResponseFunctionToolCall, + BetaResponseFunctionToolCallOutputItem, + AgentMessage, + MultiAgentCall, + MultiAgentCallOutput, + BetaResponseFunctionWebSearch, + BetaResponseComputerToolCall, + BetaResponseComputerToolCallOutputItem, + BetaResponseReasoningItem, + Program, + ProgramOutput, + BetaResponseToolSearchCall, + BetaResponseToolSearchOutputItem, + AdditionalTools, + BetaResponseCompactionItem, + ImageGenerationCall, + BetaResponseCodeInterpreterToolCall, + LocalShellCall, + LocalShellCallOutput, + BetaResponseFunctionShellToolCall, + BetaResponseFunctionShellToolCallOutput, + BetaResponseApplyPatchToolCall, + BetaResponseApplyPatchToolCallOutput, + McpCall, + McpListTools, + McpApprovalRequest, + McpApprovalResponse, + BetaResponseCustomToolCall, + BetaResponseCustomToolCallOutputItem, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/beta_response_output_item_added_event.py b/src/openai/types/beta/beta_response_output_item_added_event.py new file mode 100644 index 0000000000..ae5c3b09b2 --- /dev/null +++ b/src/openai/types/beta/beta_response_output_item_added_event.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_output_item import BetaResponseOutputItem + +__all__ = ["BetaResponseOutputItemAddedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputItemAddedEvent(BaseModel): + """Emitted when a new output item is added.""" + + item: BetaResponseOutputItem + """The output item that was added.""" + + output_index: int + """The index of the output item that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_item.added"] + """The type of the event. Always `response.output_item.added`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_output_item_done_event.py b/src/openai/types/beta/beta_response_output_item_done_event.py new file mode 100644 index 0000000000..9058adbeea --- /dev/null +++ b/src/openai/types/beta/beta_response_output_item_done_event.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_output_item import BetaResponseOutputItem + +__all__ = ["BetaResponseOutputItemDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputItemDoneEvent(BaseModel): + """Emitted when an output item is marked done.""" + + item: BetaResponseOutputItem + """The output item that was marked done.""" + + output_index: int + """The index of the output item that was marked done.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_item.done"] + """The type of the event. Always `response.output_item.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_output_message.py b/src/openai/types/beta/beta_response_output_message.py new file mode 100644 index 0000000000..9787c0817a --- /dev/null +++ b/src/openai/types/beta/beta_response_output_message.py @@ -0,0 +1,56 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_response_output_text import BetaResponseOutputText +from .beta_response_output_refusal import BetaResponseOutputRefusal + +__all__ = ["BetaResponseOutputMessage", "Content", "Agent"] + +Content: TypeAlias = Annotated[ + Union[BetaResponseOutputText, BetaResponseOutputRefusal], PropertyInfo(discriminator="type") +] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputMessage(BaseModel): + """An output message from the model.""" + + id: str + """The unique ID of the output message.""" + + content: List[Content] + """The content of the output message.""" + + role: Literal["assistant"] + """The role of the output message. Always `assistant`.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Literal["message"] + """The type of the output message. Always `message`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + phase: Optional[Literal["commentary", "final_answer"]] = None + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ diff --git a/src/openai/types/beta/beta_response_output_message_param.py b/src/openai/types/beta/beta_response_output_message_param.py new file mode 100644 index 0000000000..834a4bb1ca --- /dev/null +++ b/src/openai/types/beta/beta_response_output_message_param.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_response_output_text_param import BetaResponseOutputTextParam +from .beta_response_output_refusal_param import BetaResponseOutputRefusalParam + +__all__ = ["BetaResponseOutputMessageParam", "Content", "Agent"] + +Content: TypeAlias = Union[BetaResponseOutputTextParam, BetaResponseOutputRefusalParam] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputMessageParam(TypedDict, total=False): + """An output message from the model.""" + + id: Required[str] + """The unique ID of the output message.""" + + content: Required[Iterable[Content]] + """The content of the output message.""" + + role: Required[Literal["assistant"]] + """The role of the output message. Always `assistant`.""" + + status: Required[Literal["in_progress", "completed", "incomplete"]] + """The status of the message input. + + One of `in_progress`, `completed`, or `incomplete`. Populated when input items + are returned via API. + """ + + type: Required[Literal["message"]] + """The type of the output message. Always `message`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + phase: Optional[Literal["commentary", "final_answer"]] + """ + Labels an `assistant` message as intermediate commentary (`commentary`) or the + final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when + sending follow-up requests, preserve and resend phase on all assistant messages + — dropping it can degrade performance. Not used for user messages. + """ diff --git a/src/openai/types/beta/beta_response_output_refusal.py b/src/openai/types/beta/beta_response_output_refusal.py new file mode 100644 index 0000000000..adc3b9557f --- /dev/null +++ b/src/openai/types/beta/beta_response_output_refusal.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseOutputRefusal"] + + +class BetaResponseOutputRefusal(BaseModel): + """A refusal from the model.""" + + refusal: str + """The refusal explanation from the model.""" + + type: Literal["refusal"] + """The type of the refusal. Always `refusal`.""" diff --git a/src/openai/types/beta/beta_response_output_refusal_param.py b/src/openai/types/beta/beta_response_output_refusal_param.py new file mode 100644 index 0000000000..5f5b67e157 --- /dev/null +++ b/src/openai/types/beta/beta_response_output_refusal_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseOutputRefusalParam"] + + +class BetaResponseOutputRefusalParam(TypedDict, total=False): + """A refusal from the model.""" + + refusal: Required[str] + """The refusal explanation from the model.""" + + type: Required[Literal["refusal"]] + """The type of the refusal. Always `refusal`.""" diff --git a/src/openai/types/beta/beta_response_output_text.py b/src/openai/types/beta/beta_response_output_text.py new file mode 100644 index 0000000000..f15deb506e --- /dev/null +++ b/src/openai/types/beta/beta_response_output_text.py @@ -0,0 +1,131 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel + +__all__ = [ + "BetaResponseOutputText", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", + "Logprob", + "LogprobTopLogprob", +] + + +class AnnotationFileCitation(BaseModel): + """A citation to a file.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the file cited.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_citation"] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(BaseModel): + """A citation for a web resource used to generate a model response.""" + + end_index: int + """The index of the last character of the URL citation in the message.""" + + start_index: int + """The index of the first character of the URL citation in the message.""" + + title: str + """The title of the web resource.""" + + type: Literal["url_citation"] + """The type of the URL citation. Always `url_citation`.""" + + url: str + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(BaseModel): + """A citation for a container file used to generate a model response.""" + + container_id: str + """The ID of the container file.""" + + end_index: int + """The index of the last character of the container file citation in the message.""" + + file_id: str + """The ID of the file.""" + + filename: str + """The filename of the container file cited.""" + + start_index: int + """The index of the first character of the container file citation in the message.""" + + type: Literal["container_file_citation"] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(BaseModel): + """A path to a file.""" + + file_id: str + """The ID of the file.""" + + index: int + """The index of the file in the list of files.""" + + type: Literal["file_path"] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Annotated[ + Union[AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath], + PropertyInfo(discriminator="type"), +] + + +class LogprobTopLogprob(BaseModel): + """The top log probability of a token.""" + + token: str + + bytes: List[int] + + logprob: float + + +class Logprob(BaseModel): + """The log probability of a token.""" + + token: str + + bytes: List[int] + + logprob: float + + top_logprobs: List[LogprobTopLogprob] + + +class BetaResponseOutputText(BaseModel): + """A text output from the model.""" + + annotations: List[Annotation] + """The annotations of the text output.""" + + text: str + """The text output from the model.""" + + type: Literal["output_text"] + """The type of the output text. Always `output_text`.""" + + logprobs: Optional[List[Logprob]] = None diff --git a/src/openai/types/beta/beta_response_output_text_annotation_added_event.py b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py new file mode 100644 index 0000000000..bc4f1c227e --- /dev/null +++ b/src/openai/types/beta/beta_response_output_text_annotation_added_event.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseOutputTextAnnotationAddedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseOutputTextAnnotationAddedEvent(BaseModel): + """Emitted when an annotation is added to output text content.""" + + annotation: object + """The annotation object being added. (See annotation schema for details.)""" + + annotation_index: int + """The index of the annotation within the content part.""" + + content_index: int + """The index of the content part within the output item.""" + + item_id: str + """The unique identifier of the item to which the annotation is being added.""" + + output_index: int + """The index of the output item in the response's output array.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.output_text.annotation.added"] + """The type of the event. Always 'response.output_text.annotation.added'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_output_text_param.py b/src/openai/types/beta/beta_response_output_text_param.py new file mode 100644 index 0000000000..2acd3f6c42 --- /dev/null +++ b/src/openai/types/beta/beta_response_output_text_param.py @@ -0,0 +1,129 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = [ + "BetaResponseOutputTextParam", + "Annotation", + "AnnotationFileCitation", + "AnnotationURLCitation", + "AnnotationContainerFileCitation", + "AnnotationFilePath", + "Logprob", + "LogprobTopLogprob", +] + + +class AnnotationFileCitation(TypedDict, total=False): + """A citation to a file.""" + + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the file cited.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_citation"]] + """The type of the file citation. Always `file_citation`.""" + + +class AnnotationURLCitation(TypedDict, total=False): + """A citation for a web resource used to generate a model response.""" + + end_index: Required[int] + """The index of the last character of the URL citation in the message.""" + + start_index: Required[int] + """The index of the first character of the URL citation in the message.""" + + title: Required[str] + """The title of the web resource.""" + + type: Required[Literal["url_citation"]] + """The type of the URL citation. Always `url_citation`.""" + + url: Required[str] + """The URL of the web resource.""" + + +class AnnotationContainerFileCitation(TypedDict, total=False): + """A citation for a container file used to generate a model response.""" + + container_id: Required[str] + """The ID of the container file.""" + + end_index: Required[int] + """The index of the last character of the container file citation in the message.""" + + file_id: Required[str] + """The ID of the file.""" + + filename: Required[str] + """The filename of the container file cited.""" + + start_index: Required[int] + """The index of the first character of the container file citation in the message.""" + + type: Required[Literal["container_file_citation"]] + """The type of the container file citation. Always `container_file_citation`.""" + + +class AnnotationFilePath(TypedDict, total=False): + """A path to a file.""" + + file_id: Required[str] + """The ID of the file.""" + + index: Required[int] + """The index of the file in the list of files.""" + + type: Required[Literal["file_path"]] + """The type of the file path. Always `file_path`.""" + + +Annotation: TypeAlias = Union[ + AnnotationFileCitation, AnnotationURLCitation, AnnotationContainerFileCitation, AnnotationFilePath +] + + +class LogprobTopLogprob(TypedDict, total=False): + """The top log probability of a token.""" + + token: Required[str] + + bytes: Required[Iterable[int]] + + logprob: Required[float] + + +class Logprob(TypedDict, total=False): + """The log probability of a token.""" + + token: Required[str] + + bytes: Required[Iterable[int]] + + logprob: Required[float] + + top_logprobs: Required[Iterable[LogprobTopLogprob]] + + +class BetaResponseOutputTextParam(TypedDict, total=False): + """A text output from the model.""" + + annotations: Required[Iterable[Annotation]] + """The annotations of the text output.""" + + text: Required[str] + """The text output from the model.""" + + type: Required[Literal["output_text"]] + """The type of the output text. Always `output_text`.""" + + logprobs: Iterable[Logprob] diff --git a/src/openai/types/beta/beta_response_prompt.py b/src/openai/types/beta/beta_response_prompt.py new file mode 100644 index 0000000000..16299bda27 --- /dev/null +++ b/src/openai/types/beta/beta_response_prompt.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Union, Optional +from typing_extensions import TypeAlias + +from ..._models import BaseModel +from .beta_response_input_file import BetaResponseInputFile +from .beta_response_input_text import BetaResponseInputText +from .beta_response_input_image import BetaResponseInputImage + +__all__ = ["BetaResponsePrompt", "Variables"] + +Variables: TypeAlias = Union[str, BetaResponseInputText, BetaResponseInputImage, BetaResponseInputFile] + + +class BetaResponsePrompt(BaseModel): + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + id: str + """The unique identifier of the prompt template to use.""" + + variables: Optional[Dict[str, Variables]] = None + """Optional map of values to substitute in for variables in your prompt. + + The substitution values can either be strings, or other Response input types + like images or files. + """ + + version: Optional[str] = None + """Optional version of the prompt template.""" diff --git a/src/openai/types/beta/beta_response_prompt_param.py b/src/openai/types/beta/beta_response_prompt_param.py new file mode 100644 index 0000000000..1bd2941875 --- /dev/null +++ b/src/openai/types/beta/beta_response_prompt_param.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from typing_extensions import Required, TypeAlias, TypedDict + +from .beta_response_input_file_param import BetaResponseInputFileParam +from .beta_response_input_text_param import BetaResponseInputTextParam +from .beta_response_input_image_param import BetaResponseInputImageParam + +__all__ = ["BetaResponsePromptParam", "Variables"] + +Variables: TypeAlias = Union[str, BetaResponseInputTextParam, BetaResponseInputImageParam, BetaResponseInputFileParam] + + +class BetaResponsePromptParam(TypedDict, total=False): + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + id: Required[str] + """The unique identifier of the prompt template to use.""" + + variables: Optional[Dict[str, Variables]] + """Optional map of values to substitute in for variables in your prompt. + + The substitution values can either be strings, or other Response input types + like images or files. + """ + + version: Optional[str] + """Optional version of the prompt template.""" diff --git a/src/openai/types/beta/beta_response_queued_event.py b/src/openai/types/beta/beta_response_queued_event.py new file mode 100644 index 0000000000..de322b037f --- /dev/null +++ b/src/openai/types/beta/beta_response_queued_event.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response import BetaResponse + +__all__ = ["BetaResponseQueuedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseQueuedEvent(BaseModel): + """Emitted when a response is queued and waiting to be processed.""" + + response: BetaResponse + """The full response object that is queued.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.queued"] + """The type of the event. Always 'response.queued'.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_reasoning_item.py b/src/openai/types/beta/beta_response_reasoning_item.py new file mode 100644 index 0000000000..57bed79f63 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_item.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningItem", "Summary", "Agent", "Content"] + + +class Summary(BaseModel): + """A summary text from the model.""" + + text: str + """A summary of the reasoning output from the model so far.""" + + type: Literal["summary_text"] + """The type of the object. Always `summary_text`.""" + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class Content(BaseModel): + """Reasoning text from the model.""" + + text: str + """The reasoning text from the model.""" + + type: Literal["reasoning_text"] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class BetaResponseReasoningItem(BaseModel): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://platform.openai.com/docs/guides/conversation-state). + """ + + id: str + """The unique identifier of the reasoning content.""" + + summary: List[Summary] + """Reasoning summary content.""" + + type: Literal["reasoning"] + """The type of the object. Always `reasoning`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + content: Optional[List[Content]] = None + """Reasoning text content.""" + + encrypted_content: Optional[str] = None + """ + The encrypted content of the reasoning item - populated when a response is + generated with `reasoning.encrypted_content` in the `include` parameter. + """ + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/beta/beta_response_reasoning_item_param.py b/src/openai/types/beta/beta_response_reasoning_item_param.py new file mode 100644 index 0000000000..8a91128af8 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_item_param.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaResponseReasoningItemParam", "Summary", "Agent", "Content"] + + +class Summary(TypedDict, total=False): + """A summary text from the model.""" + + text: Required[str] + """A summary of the reasoning output from the model so far.""" + + type: Required[Literal["summary_text"]] + """The type of the object. Always `summary_text`.""" + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class Content(TypedDict, total=False): + """Reasoning text from the model.""" + + text: Required[str] + """The reasoning text from the model.""" + + type: Required[Literal["reasoning_text"]] + """The type of the reasoning text. Always `reasoning_text`.""" + + +class BetaResponseReasoningItemParam(TypedDict, total=False): + """ + A description of the chain of thought used by a reasoning model while generating + a response. Be sure to include these items in your `input` to the Responses API + for subsequent turns of a conversation if you are manually + [managing context](https://platform.openai.com/docs/guides/conversation-state). + """ + + id: Required[str] + """The unique identifier of the reasoning content.""" + + summary: Required[Iterable[Summary]] + """Reasoning summary content.""" + + type: Required[Literal["reasoning"]] + """The type of the object. Always `reasoning`.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + content: Iterable[Content] + """Reasoning text content.""" + + encrypted_content: Optional[str] + """ + The encrypted content of the reasoning item - populated when a response is + generated with `reasoning.encrypted_content` in the `include` parameter. + """ + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the item. + + One of `in_progress`, `completed`, or `incomplete`. Populated when items are + returned via API. + """ diff --git a/src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py b/src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py new file mode 100644 index 0000000000..99366b8970 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_summary_part_added_event.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryPartAddedEvent", "Part", "Agent"] + + +class Part(BaseModel): + """The summary part that was added.""" + + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryPartAddedEvent(BaseModel): + """Emitted when a new reasoning summary part is added.""" + + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The summary part that was added.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.added"] + """The type of the event. Always `response.reasoning_summary_part.added`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py b/src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py new file mode 100644 index 0000000000..f7c0a67e15 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_summary_part_done_event.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryPartDoneEvent", "Part", "Agent"] + + +class Part(BaseModel): + """The completed summary part.""" + + text: str + """The text of the summary part.""" + + type: Literal["summary_text"] + """The type of the summary part. Always `summary_text`.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryPartDoneEvent(BaseModel): + """Emitted when a reasoning summary part is completed.""" + + item_id: str + """The ID of the item this summary part is associated with.""" + + output_index: int + """The index of the output item this summary part is associated with.""" + + part: Part + """The completed summary part.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_part.done"] + """The type of the event. Always `response.reasoning_summary_part.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" + + status: Optional[Literal["incomplete"]] = None + """The completion status of the summary part. + + Omitted when the part completed normally and set to `incomplete` when generation + was interrupted. + """ diff --git a/src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py b/src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py new file mode 100644 index 0000000000..c4dfc01fe8 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_summary_text_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryTextDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning summary text.""" + + delta: str + """The text delta that was added to the summary.""" + + item_id: str + """The ID of the item this summary text delta is associated with.""" + + output_index: int + """The index of the output item this summary text delta is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + type: Literal["response.reasoning_summary_text.delta"] + """The type of the event. Always `response.reasoning_summary_text.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py b/src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py new file mode 100644 index 0000000000..2744b72cd5 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_summary_text_done_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningSummaryTextDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningSummaryTextDoneEvent(BaseModel): + """Emitted when a reasoning summary text is completed.""" + + item_id: str + """The ID of the item this summary text is associated with.""" + + output_index: int + """The index of the output item this summary text is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + summary_index: int + """The index of the summary part within the reasoning summary.""" + + text: str + """The full text of the completed reasoning summary.""" + + type: Literal["response.reasoning_summary_text.done"] + """The type of the event. Always `response.reasoning_summary_text.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_reasoning_text_delta_event.py b/src/openai/types/beta/beta_response_reasoning_text_delta_event.py new file mode 100644 index 0000000000..aa0713cef8 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_text_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningTextDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningTextDeltaEvent(BaseModel): + """Emitted when a delta is added to a reasoning text.""" + + content_index: int + """The index of the reasoning content part this delta is associated with.""" + + delta: str + """The text delta that was added to the reasoning content.""" + + item_id: str + """The ID of the item this reasoning text delta is associated with.""" + + output_index: int + """The index of the output item this reasoning text delta is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.reasoning_text.delta"] + """The type of the event. Always `response.reasoning_text.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_reasoning_text_done_event.py b/src/openai/types/beta/beta_response_reasoning_text_done_event.py new file mode 100644 index 0000000000..465e175db9 --- /dev/null +++ b/src/openai/types/beta/beta_response_reasoning_text_done_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseReasoningTextDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseReasoningTextDoneEvent(BaseModel): + """Emitted when a reasoning text is completed.""" + + content_index: int + """The index of the reasoning content part.""" + + item_id: str + """The ID of the item this reasoning text is associated with.""" + + output_index: int + """The index of the output item this reasoning text is associated with.""" + + sequence_number: int + """The sequence number of this event.""" + + text: str + """The full text of the completed reasoning content.""" + + type: Literal["response.reasoning_text.done"] + """The type of the event. Always `response.reasoning_text.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_refusal_delta_event.py b/src/openai/types/beta/beta_response_refusal_delta_event.py new file mode 100644 index 0000000000..ecccd8990d --- /dev/null +++ b/src/openai/types/beta/beta_response_refusal_delta_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseRefusalDeltaEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseRefusalDeltaEvent(BaseModel): + """Emitted when there is a partial refusal text.""" + + content_index: int + """The index of the content part that the refusal text is added to.""" + + delta: str + """The refusal text that is added.""" + + item_id: str + """The ID of the output item that the refusal text is added to.""" + + output_index: int + """The index of the output item that the refusal text is added to.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.refusal.delta"] + """The type of the event. Always `response.refusal.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_refusal_done_event.py b/src/openai/types/beta/beta_response_refusal_done_event.py new file mode 100644 index 0000000000..d6d6574d8c --- /dev/null +++ b/src/openai/types/beta/beta_response_refusal_done_event.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseRefusalDoneEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseRefusalDoneEvent(BaseModel): + """Emitted when refusal text is finalized.""" + + content_index: int + """The index of the content part that the refusal text is finalized.""" + + item_id: str + """The ID of the output item that the refusal text is finalized.""" + + output_index: int + """The index of the output item that the refusal text is finalized.""" + + refusal: str + """The refusal text that is finalized.""" + + sequence_number: int + """The sequence number of this event.""" + + type: Literal["response.refusal.done"] + """The type of the event. Always `response.refusal.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_status.py b/src/openai/types/beta/beta_response_status.py new file mode 100644 index 0000000000..c1ea0379b3 --- /dev/null +++ b/src/openai/types/beta/beta_response_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaResponseStatus"] + +BetaResponseStatus: TypeAlias = Literal["completed", "failed", "in_progress", "cancelled", "queued", "incomplete"] diff --git a/src/openai/types/beta/beta_response_stream_event.py b/src/openai/types/beta/beta_response_stream_event.py new file mode 100644 index 0000000000..397e1368b5 --- /dev/null +++ b/src/openai/types/beta/beta_response_stream_event.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_error_event import BetaResponseErrorEvent +from .beta_response_failed_event import BetaResponseFailedEvent +from .beta_response_queued_event import BetaResponseQueuedEvent +from .beta_response_created_event import BetaResponseCreatedEvent +from .beta_response_completed_event import BetaResponseCompletedEvent +from .beta_response_text_done_event import BetaResponseTextDoneEvent +from .beta_response_audio_done_event import BetaResponseAudioDoneEvent +from .beta_response_incomplete_event import BetaResponseIncompleteEvent +from .beta_response_text_delta_event import BetaResponseTextDeltaEvent +from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent +from .beta_response_in_progress_event import BetaResponseInProgressEvent +from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent +from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent +from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent +from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent +from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent +from .beta_response_output_item_added_event import BetaResponseOutputItemAddedEvent +from .beta_response_content_part_added_event import BetaResponseContentPartAddedEvent +from .beta_response_mcp_call_completed_event import BetaResponseMcpCallCompletedEvent +from .beta_response_reasoning_text_done_event import BetaResponseReasoningTextDoneEvent +from .beta_response_mcp_call_in_progress_event import BetaResponseMcpCallInProgressEvent +from .beta_response_reasoning_text_delta_event import BetaResponseReasoningTextDeltaEvent +from .beta_response_audio_transcript_done_event import BetaResponseAudioTranscriptDoneEvent +from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent +from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent +from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent +from .beta_response_image_gen_call_completed_event import BetaResponseImageGenCallCompletedEvent +from .beta_response_mcp_call_arguments_delta_event import BetaResponseMcpCallArgumentsDeltaEvent +from .beta_response_mcp_list_tools_completed_event import BetaResponseMcpListToolsCompletedEvent +from .beta_response_image_gen_call_generating_event import BetaResponseImageGenCallGeneratingEvent +from .beta_response_web_search_call_completed_event import BetaResponseWebSearchCallCompletedEvent +from .beta_response_web_search_call_searching_event import BetaResponseWebSearchCallSearchingEvent +from .beta_response_file_search_call_completed_event import BetaResponseFileSearchCallCompletedEvent +from .beta_response_file_search_call_searching_event import BetaResponseFileSearchCallSearchingEvent +from .beta_response_image_gen_call_in_progress_event import BetaResponseImageGenCallInProgressEvent +from .beta_response_mcp_list_tools_in_progress_event import BetaResponseMcpListToolsInProgressEvent +from .beta_response_custom_tool_call_input_done_event import BetaResponseCustomToolCallInputDoneEvent +from .beta_response_reasoning_summary_part_done_event import BetaResponseReasoningSummaryPartDoneEvent +from .beta_response_reasoning_summary_text_done_event import BetaResponseReasoningSummaryTextDoneEvent +from .beta_response_web_search_call_in_progress_event import BetaResponseWebSearchCallInProgressEvent +from .beta_response_custom_tool_call_input_delta_event import BetaResponseCustomToolCallInputDeltaEvent +from .beta_response_file_search_call_in_progress_event import BetaResponseFileSearchCallInProgressEvent +from .beta_response_function_call_arguments_done_event import BetaResponseFunctionCallArgumentsDoneEvent +from .beta_response_image_gen_call_partial_image_event import BetaResponseImageGenCallPartialImageEvent +from .beta_response_output_text_annotation_added_event import BetaResponseOutputTextAnnotationAddedEvent +from .beta_response_reasoning_summary_part_added_event import BetaResponseReasoningSummaryPartAddedEvent +from .beta_response_reasoning_summary_text_delta_event import BetaResponseReasoningSummaryTextDeltaEvent +from .beta_response_function_call_arguments_delta_event import BetaResponseFunctionCallArgumentsDeltaEvent +from .beta_response_code_interpreter_call_code_done_event import BetaResponseCodeInterpreterCallCodeDoneEvent +from .beta_response_code_interpreter_call_completed_event import BetaResponseCodeInterpreterCallCompletedEvent +from .beta_response_code_interpreter_call_code_delta_event import BetaResponseCodeInterpreterCallCodeDeltaEvent +from .beta_response_code_interpreter_call_in_progress_event import BetaResponseCodeInterpreterCallInProgressEvent +from .beta_response_code_interpreter_call_interpreting_event import BetaResponseCodeInterpreterCallInterpretingEvent + +__all__ = ["BetaResponseStreamEvent"] + +BetaResponseStreamEvent: TypeAlias = Annotated[ + Union[ + BetaResponseAudioDeltaEvent, + BetaResponseAudioDoneEvent, + BetaResponseAudioTranscriptDeltaEvent, + BetaResponseAudioTranscriptDoneEvent, + BetaResponseCodeInterpreterCallCodeDeltaEvent, + BetaResponseCodeInterpreterCallCodeDoneEvent, + BetaResponseCodeInterpreterCallCompletedEvent, + BetaResponseCodeInterpreterCallInProgressEvent, + BetaResponseCodeInterpreterCallInterpretingEvent, + BetaResponseCompletedEvent, + BetaResponseContentPartAddedEvent, + BetaResponseContentPartDoneEvent, + BetaResponseCreatedEvent, + BetaResponseErrorEvent, + BetaResponseFileSearchCallCompletedEvent, + BetaResponseFileSearchCallInProgressEvent, + BetaResponseFileSearchCallSearchingEvent, + BetaResponseFunctionCallArgumentsDeltaEvent, + BetaResponseFunctionCallArgumentsDoneEvent, + BetaResponseInProgressEvent, + BetaResponseFailedEvent, + BetaResponseIncompleteEvent, + BetaResponseOutputItemAddedEvent, + BetaResponseOutputItemDoneEvent, + BetaResponseReasoningSummaryPartAddedEvent, + BetaResponseReasoningSummaryPartDoneEvent, + BetaResponseReasoningSummaryTextDeltaEvent, + BetaResponseReasoningSummaryTextDoneEvent, + BetaResponseReasoningTextDeltaEvent, + BetaResponseReasoningTextDoneEvent, + BetaResponseRefusalDeltaEvent, + BetaResponseRefusalDoneEvent, + BetaResponseTextDeltaEvent, + BetaResponseTextDoneEvent, + BetaResponseWebSearchCallCompletedEvent, + BetaResponseWebSearchCallInProgressEvent, + BetaResponseWebSearchCallSearchingEvent, + BetaResponseImageGenCallCompletedEvent, + BetaResponseImageGenCallGeneratingEvent, + BetaResponseImageGenCallInProgressEvent, + BetaResponseImageGenCallPartialImageEvent, + BetaResponseMcpCallArgumentsDeltaEvent, + BetaResponseMcpCallArgumentsDoneEvent, + BetaResponseMcpCallCompletedEvent, + BetaResponseMcpCallFailedEvent, + BetaResponseMcpCallInProgressEvent, + BetaResponseMcpListToolsCompletedEvent, + BetaResponseMcpListToolsFailedEvent, + BetaResponseMcpListToolsInProgressEvent, + BetaResponseOutputTextAnnotationAddedEvent, + BetaResponseQueuedEvent, + BetaResponseCustomToolCallInputDeltaEvent, + BetaResponseCustomToolCallInputDoneEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/beta_response_text_config.py b/src/openai/types/beta/beta_response_text_config.py new file mode 100644 index 0000000000..a0ec4e66d9 --- /dev/null +++ b/src/openai/types/beta/beta_response_text_config.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_response_format_text_config import BetaResponseFormatTextConfig + +__all__ = ["BetaResponseTextConfig"] + + +class BetaResponseTextConfig(BaseModel): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + format: Optional[BetaResponseFormatTextConfig] = None + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] = None + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ diff --git a/src/openai/types/beta/beta_response_text_config_param.py b/src/openai/types/beta/beta_response_text_config_param.py new file mode 100644 index 0000000000..52f2cd6a1a --- /dev/null +++ b/src/openai/types/beta/beta_response_text_config_param.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, TypedDict + +from .beta_response_format_text_config_param import BetaResponseFormatTextConfigParam + +__all__ = ["BetaResponseTextConfigParam"] + + +class BetaResponseTextConfigParam(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + format: BetaResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ diff --git a/src/openai/types/beta/beta_response_text_delta_event.py b/src/openai/types/beta/beta_response_text_delta_event.py new file mode 100644 index 0000000000..3c22a6cfcc --- /dev/null +++ b/src/openai/types/beta/beta_response_text_delta_event.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseTextDeltaEvent", "Logprob", "LogprobTopLogprob", "Agent"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probabilities of up to 20 of the most likely tokens.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseTextDeltaEvent(BaseModel): + """Emitted when there is an additional text delta.""" + + content_index: int + """The index of the content part that the text delta was added to.""" + + delta: str + """The text delta that was added.""" + + item_id: str + """The ID of the output item that the text delta was added to.""" + + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + + output_index: int + """The index of the output item that the text delta was added to.""" + + sequence_number: int + """The sequence number for this event.""" + + type: Literal["response.output_text.delta"] + """The type of the event. Always `response.output_text.delta`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_text_done_event.py b/src/openai/types/beta/beta_response_text_done_event.py new file mode 100644 index 0000000000..b56c784568 --- /dev/null +++ b/src/openai/types/beta/beta_response_text_done_event.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseTextDoneEvent", "Logprob", "LogprobTopLogprob", "Agent"] + + +class LogprobTopLogprob(BaseModel): + token: Optional[str] = None + """A possible text token.""" + + logprob: Optional[float] = None + """The log probability of this token.""" + + +class Logprob(BaseModel): + """ + A logprob is the logarithmic probability that the model assigns to producing + a particular token at a given position in the sequence. Less-negative (higher) + logprob values indicate greater model confidence in that token choice. + """ + + token: str + """A possible text token.""" + + logprob: float + """The log probability of this token.""" + + top_logprobs: Optional[List[LogprobTopLogprob]] = None + """The log probabilities of up to 20 of the most likely tokens.""" + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseTextDoneEvent(BaseModel): + """Emitted when text content is finalized.""" + + content_index: int + """The index of the content part that the text content is finalized.""" + + item_id: str + """The ID of the output item that the text content is finalized.""" + + logprobs: List[Logprob] + """The log probabilities of the tokens in the delta.""" + + output_index: int + """The index of the output item that the text content is finalized.""" + + sequence_number: int + """The sequence number for this event.""" + + text: str + """The text content that is finalized.""" + + type: Literal["response.output_text.done"] + """The type of the event. Always `response.output_text.done`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_tool_search_call.py b/src/openai/types/beta/beta_response_tool_search_call.py new file mode 100644 index 0000000000..eb71bc6f69 --- /dev/null +++ b/src/openai/types/beta/beta_response_tool_search_call.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseToolSearchCall", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchCall(BaseModel): + id: str + """The unique ID of the tool search call item.""" + + arguments: object + """Arguments used for the tool search call.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search call item that was recorded.""" + + type: Literal["tool_search_call"] + """The type of the item. Always `tool_search_call`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_tool_search_output_item.py b/src/openai/types/beta/beta_response_tool_search_output_item.py new file mode 100644 index 0000000000..408be2338c --- /dev/null +++ b/src/openai/types/beta/beta_response_tool_search_output_item.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_tool import BetaTool + +__all__ = ["BetaResponseToolSearchOutputItem", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchOutputItem(BaseModel): + id: str + """The unique ID of the tool search output item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Literal["in_progress", "completed", "incomplete"] + """The status of the tool search output item that was recorded.""" + + tools: List[BetaTool] + """The loaded tool definitions returned by tool search.""" + + type: Literal["tool_search_output"] + """The type of the item. Always `tool_search_output`.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + created_by: Optional[str] = None + """The identifier of the actor that created the item.""" diff --git a/src/openai/types/beta/beta_response_tool_search_output_item_param.py b/src/openai/types/beta/beta_response_tool_search_output_item_param.py new file mode 100644 index 0000000000..6d6e6e4b3a --- /dev/null +++ b/src/openai/types/beta/beta_response_tool_search_output_item_param.py @@ -0,0 +1,39 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .beta_tool import BetaTool + +__all__ = ["BetaResponseToolSearchOutputItemParam", "Agent"] + + +class Agent(BaseModel): + """The agent that produced this item.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchOutputItemParam(BaseModel): + tools: List[BetaTool] + """The loaded tool definitions returned by the tool search output.""" + + type: Literal["tool_search_output"] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] = None + """The unique ID of this tool search output.""" + + agent: Optional[Agent] = None + """The agent that produced this item.""" + + call_id: Optional[str] = None + """The unique ID of the tool search call generated by the model.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None + """The status of the tool search output.""" diff --git a/src/openai/types/beta/beta_response_tool_search_output_item_param_param.py b/src/openai/types/beta/beta_response_tool_search_output_item_param_param.py new file mode 100644 index 0000000000..b2864bdfeb --- /dev/null +++ b/src/openai/types/beta/beta_response_tool_search_output_item_param_param.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable, Optional +from typing_extensions import Literal, Required, TypedDict + +from .beta_tool_param import BetaToolParam + +__all__ = ["BetaResponseToolSearchOutputItemParamParam", "Agent"] + + +class Agent(TypedDict, total=False): + """The agent that produced this item.""" + + agent_name: Required[str] + """The canonical name of the agent that produced this item.""" + + +class BetaResponseToolSearchOutputItemParamParam(TypedDict, total=False): + tools: Required[Iterable[BetaToolParam]] + """The loaded tool definitions returned by the tool search output.""" + + type: Required[Literal["tool_search_output"]] + """The item type. Always `tool_search_output`.""" + + id: Optional[str] + """The unique ID of this tool search output.""" + + agent: Optional[Agent] + """The agent that produced this item.""" + + call_id: Optional[str] + """The unique ID of the tool search call generated by the model.""" + + execution: Literal["server", "client"] + """Whether tool search was executed by the server or by the client.""" + + status: Optional[Literal["in_progress", "completed", "incomplete"]] + """The status of the tool search output.""" diff --git a/src/openai/types/beta/beta_response_usage.py b/src/openai/types/beta/beta_response_usage.py new file mode 100644 index 0000000000..4992c89a47 --- /dev/null +++ b/src/openai/types/beta/beta_response_usage.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["BetaResponseUsage", "InputTokensDetails", "OutputTokensDetails"] + + +class InputTokensDetails(BaseModel): + """A detailed breakdown of the input tokens.""" + + cache_write_tokens: int + """The number of input tokens that were written to the cache.""" + + cached_tokens: int + """The number of tokens that were retrieved from the cache. + + [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching). + """ + + +class OutputTokensDetails(BaseModel): + """A detailed breakdown of the output tokens.""" + + reasoning_tokens: int + """The number of reasoning tokens.""" + + +class BetaResponseUsage(BaseModel): + """ + Represents token usage details including input tokens, output tokens, + a breakdown of output tokens, and the total tokens used. + """ + + input_tokens: int + """The number of input tokens.""" + + input_tokens_details: InputTokensDetails + """A detailed breakdown of the input tokens.""" + + output_tokens: int + """The number of output tokens.""" + + output_tokens_details: OutputTokensDetails + """A detailed breakdown of the output tokens.""" + + total_tokens: int + """The total number of tokens used.""" diff --git a/src/openai/types/beta/beta_response_web_search_call_completed_event.py b/src/openai/types/beta/beta_response_web_search_call_completed_event.py new file mode 100644 index 0000000000..000e589830 --- /dev/null +++ b/src/openai/types/beta/beta_response_web_search_call_completed_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseWebSearchCallCompletedEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseWebSearchCallCompletedEvent(BaseModel): + """Emitted when a web search call is completed.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.completed"] + """The type of the event. Always `response.web_search_call.completed`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_web_search_call_in_progress_event.py b/src/openai/types/beta/beta_response_web_search_call_in_progress_event.py new file mode 100644 index 0000000000..75080cdbb0 --- /dev/null +++ b/src/openai/types/beta/beta_response_web_search_call_in_progress_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseWebSearchCallInProgressEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseWebSearchCallInProgressEvent(BaseModel): + """Emitted when a web search call is initiated.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.in_progress"] + """The type of the event. Always `response.web_search_call.in_progress`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_response_web_search_call_searching_event.py b/src/openai/types/beta/beta_response_web_search_call_searching_event.py new file mode 100644 index 0000000000..39027d6363 --- /dev/null +++ b/src/openai/types/beta/beta_response_web_search_call_searching_event.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaResponseWebSearchCallSearchingEvent", "Agent"] + + +class Agent(BaseModel): + """The agent that owns this multi-agent streaming event.""" + + agent_name: str + """The canonical name of the agent that produced this item.""" + + +class BetaResponseWebSearchCallSearchingEvent(BaseModel): + """Emitted when a web search call is executing.""" + + item_id: str + """Unique ID for the output item associated with the web search call.""" + + output_index: int + """The index of the output item that the web search call is associated with.""" + + sequence_number: int + """The sequence number of the web search call being processed.""" + + type: Literal["response.web_search_call.searching"] + """The type of the event. Always `response.web_search_call.searching`.""" + + agent: Optional[Agent] = None + """The agent that owns this multi-agent streaming event.""" diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py new file mode 100644 index 0000000000..6450d15260 --- /dev/null +++ b/src/openai/types/beta/beta_responses_client_event.py @@ -0,0 +1,619 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_tool import BetaTool +from .beta_response_input import BetaResponseInput +from .beta_response_prompt import BetaResponsePrompt +from .beta_tool_choice_mcp import BetaToolChoiceMcp +from .beta_tool_choice_shell import BetaToolChoiceShell +from .beta_tool_choice_types import BetaToolChoiceTypes +from .beta_tool_choice_custom import BetaToolChoiceCustom +from .beta_response_includable import BetaResponseIncludable +from .beta_tool_choice_allowed import BetaToolChoiceAllowed +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_text_config import BetaResponseTextConfig +from .beta_tool_choice_function import BetaToolChoiceFunction +from .beta_response_inject_event import BetaResponseInjectEvent +from .beta_tool_choice_apply_patch import BetaToolChoiceApplyPatch +from .beta_response_conversation_param import BetaResponseConversationParam + +__all__ = [ + "BetaResponsesClientEvent", + "ResponseCreate", + "ResponseCreateContextManagement", + "ResponseCreateConversation", + "ResponseCreateModeration", + "ResponseCreateModerationPolicy", + "ResponseCreateModerationPolicyInput", + "ResponseCreateModerationPolicyOutput", + "ResponseCreateMultiAgent", + "ResponseCreatePromptCacheOptions", + "ResponseCreateReasoning", + "ResponseCreateStreamOptions", + "ResponseCreateToolChoice", + "ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam", +] + + +class ResponseCreateContextManagement(BaseModel): + type: str + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] = None + """Token threshold at which compaction should be triggered for this entry.""" + + +ResponseCreateConversation: TypeAlias = Union[str, BetaResponseConversationParam, None] + + +class ResponseCreateModerationPolicyInput(BaseModel): + """The moderation policy for the response input.""" + + mode: Literal["score", "block"] + + +class ResponseCreateModerationPolicyOutput(BaseModel): + """The moderation policy for the response output.""" + + mode: Literal["score", "block"] + + +class ResponseCreateModerationPolicy(BaseModel): + """The policy to apply to moderated response input and output.""" + + input: Optional[ResponseCreateModerationPolicyInput] = None + """The moderation policy for the response input.""" + + output: Optional[ResponseCreateModerationPolicyOutput] = None + """The moderation policy for the response output.""" + + +class ResponseCreateModeration(BaseModel): + """Configuration for running moderation on the input and output of this response.""" + + model: str + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ResponseCreateModerationPolicy] = None + """The policy to apply to moderated response input and output.""" + + +class ResponseCreateMultiAgent(BaseModel): + """Configuration for server-hosted multi-agent execution.""" + + enabled: bool + """Whether to enable server-hosted multi-agent execution for this response.""" + + max_concurrent_subagents: Optional[int] = None + """ + `max_concurrent_subagents` sets the maximum number of subagents that can be + active simultaneously across the entire agent tree. It includes all + descendants—children, grandchildren, and deeper subagents—but excludes the root + agent. The API does not impose a fixed upper bound on this setting. The default + is `3`, which is recommended for most workloads. Multi-agent runs also have no + fixed limit on tree depth or the total number of subagents created during a run. + """ + + +class ResponseCreatePromptCacheOptions(BaseModel): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Optional[Literal["implicit", "explicit"]] = None + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Optional[Literal["30m"]] = None + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class ResponseCreateReasoning(BaseModel): + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] = None + """ + Controls which reasoning items are rendered back to the model on later turns. + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] = None + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"], None] = None + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] = None + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class ResponseCreateStreamOptions(BaseModel): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: Optional[bool] = None + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ResponseCreateToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowed, + BetaToolChoiceTypes, + BetaToolChoiceFunction, + BetaToolChoiceMcp, + BetaToolChoiceCustom, + ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatch, + BetaToolChoiceShell, +] + + +class ResponseCreate(BaseModel): + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + """ + + type: Literal["response.create"] + """The type of the client event. Always `response.create`.""" + + background: Optional[bool] = None + """ + Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + """ + + context_management: Optional[List[ResponseCreateContextManagement]] = None + """Context management configuration for this request.""" + + conversation: Optional[ResponseCreateConversation] = None + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[BetaResponseIncludable]] = None + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, BetaResponseInput, None] = None + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + """ + + instructions: Optional[str] = None + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] = None + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] = None + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Dict[str, str]] = None + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ] = None + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + moderation: Optional[ResponseCreateModeration] = None + """Configuration for running moderation on the input and output of this response.""" + + multi_agent: Optional[ResponseCreateMultiAgent] = None + """Configuration for server-hosted multi-agent execution.""" + + parallel_tool_calls: Optional[bool] = None + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] = None + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePrompt] = None + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + prompt_cache_key: Optional[str] = None + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + + prompt_cache_options: Optional[ResponseCreatePromptCacheOptions] = None + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[ResponseCreateReasoning] = None + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + safety_identifier: Optional[str] = None + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] = None + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] = None + """Whether to store the generated model response for later retrieval via API.""" + + stream: Optional[bool] = None + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + stream_options: Optional[ResponseCreateStreamOptions] = None + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] = None + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: Optional[BetaResponseTextConfig] = None + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ResponseCreateToolChoice] = None + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Optional[List[BetaTool]] = None + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] = None + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] = None + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] = None + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: Optional[str] = None + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + +BetaResponsesClientEvent: TypeAlias = Annotated[ + Union[ResponseCreate, BetaResponseInjectEvent], PropertyInfo(discriminator="type") +] diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py new file mode 100644 index 0000000000..adc3e958ac --- /dev/null +++ b/src/openai/types/beta/beta_responses_client_event_param.py @@ -0,0 +1,616 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from .beta_tool_param import BetaToolParam +from .beta_response_includable import BetaResponseIncludable +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_input_param import BetaResponseInputParam +from .beta_response_prompt_param import BetaResponsePromptParam +from .beta_tool_choice_mcp_param import BetaToolChoiceMcpParam +from .beta_tool_choice_shell_param import BetaToolChoiceShellParam +from .beta_tool_choice_types_param import BetaToolChoiceTypesParam +from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam +from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam +from .beta_response_text_config_param import BetaResponseTextConfigParam +from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam +from .beta_response_inject_event_param import BetaResponseInjectEventParam +from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam +from .beta_response_conversation_param_param import BetaResponseConversationParamParam + +__all__ = [ + "BetaResponsesClientEventParam", + "ResponseCreate", + "ResponseCreateContextManagement", + "ResponseCreateConversation", + "ResponseCreateModeration", + "ResponseCreateModerationPolicy", + "ResponseCreateModerationPolicyInput", + "ResponseCreateModerationPolicyOutput", + "ResponseCreateMultiAgent", + "ResponseCreatePromptCacheOptions", + "ResponseCreateReasoning", + "ResponseCreateStreamOptions", + "ResponseCreateToolChoice", + "ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam", +] + + +class ResponseCreateContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + +ResponseCreateConversation: TypeAlias = Union[str, BetaResponseConversationParamParam] + + +class ResponseCreateModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ResponseCreateModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ResponseCreateModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ResponseCreateModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ResponseCreateModerationPolicyOutput] + """The moderation policy for the response output.""" + + +class ResponseCreateModeration(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ResponseCreateModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class ResponseCreateMultiAgent(TypedDict, total=False): + """Configuration for server-hosted multi-agent execution.""" + + enabled: Required[bool] + """Whether to enable server-hosted multi-agent execution for this response.""" + + max_concurrent_subagents: int + """ + `max_concurrent_subagents` sets the maximum number of subagents that can be + active simultaneously across the entire agent tree. It includes all + descendants—children, grandchildren, and deeper subagents—but excludes the root + agent. The API does not impose a fixed upper bound on this setting. The default + is `3`, which is recommended for most workloads. Multi-agent runs also have no + fixed limit on tree depth or the total number of subagents created during a run. + """ + + +class ResponseCreatePromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class ResponseCreateReasoning(TypedDict, total=False): + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class ResponseCreateStreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ResponseCreateToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowedParam, + BetaToolChoiceTypesParam, + BetaToolChoiceFunctionParam, + BetaToolChoiceMcpParam, + BetaToolChoiceCustomParam, + ResponseCreateToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatchParam, + BetaToolChoiceShellParam, +] + + +class ResponseCreate(TypedDict, total=False): + """ + Client event for creating a response over a persistent WebSocket connection. + This payload uses the same top-level fields as `POST /v1/responses`. + + Notes: + - `stream` is implicit over WebSocket and should not be sent. + - `background` is not supported over WebSocket. + """ + + type: Required[Literal["response.create"]] + """The type of the client event. Always `response.create`.""" + + background: Optional[bool] + """ + Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + """ + + context_management: Optional[Iterable[ResponseCreateContextManagement]] + """Context management configuration for this request.""" + + conversation: Optional[ResponseCreateConversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[BetaResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, BetaResponseInputParam] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + """ + + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Dict[str, str]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + moderation: Optional[ResponseCreateModeration] + """Configuration for running moderation on the input and output of this response.""" + + multi_agent: Optional[ResponseCreateMultiAgent] + """Configuration for server-hosted multi-agent execution.""" + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + prompt_cache_key: str + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + + prompt_cache_options: ResponseCreatePromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[ResponseCreateReasoning] + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + safety_identifier: str + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API.""" + + stream: Optional[bool] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + stream_options: Optional[ResponseCreateStreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: BetaResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: ResponseCreateToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[BetaToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + +BetaResponsesClientEventParam: TypeAlias = Union[ResponseCreate, BetaResponseInjectEventParam] diff --git a/src/openai/types/beta/beta_responses_server_event.py b/src/openai/types/beta/beta_responses_server_event.py new file mode 100644 index 0000000000..945561cfb4 --- /dev/null +++ b/src/openai/types/beta/beta_responses_server_event.py @@ -0,0 +1,124 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Annotated, TypeAlias + +from ..._utils import PropertyInfo +from .beta_response_error_event import BetaResponseErrorEvent +from .beta_response_failed_event import BetaResponseFailedEvent +from .beta_response_queued_event import BetaResponseQueuedEvent +from .beta_response_created_event import BetaResponseCreatedEvent +from .beta_response_completed_event import BetaResponseCompletedEvent +from .beta_response_text_done_event import BetaResponseTextDoneEvent +from .beta_response_audio_done_event import BetaResponseAudioDoneEvent +from .beta_response_incomplete_event import BetaResponseIncompleteEvent +from .beta_response_text_delta_event import BetaResponseTextDeltaEvent +from .beta_response_audio_delta_event import BetaResponseAudioDeltaEvent +from .beta_response_in_progress_event import BetaResponseInProgressEvent +from .beta_response_refusal_done_event import BetaResponseRefusalDoneEvent +from .beta_response_inject_failed_event import BetaResponseInjectFailedEvent +from .beta_response_refusal_delta_event import BetaResponseRefusalDeltaEvent +from .beta_response_inject_created_event import BetaResponseInjectCreatedEvent +from .beta_response_mcp_call_failed_event import BetaResponseMcpCallFailedEvent +from .beta_response_output_item_done_event import BetaResponseOutputItemDoneEvent +from .beta_response_content_part_done_event import BetaResponseContentPartDoneEvent +from .beta_response_output_item_added_event import BetaResponseOutputItemAddedEvent +from .beta_response_content_part_added_event import BetaResponseContentPartAddedEvent +from .beta_response_mcp_call_completed_event import BetaResponseMcpCallCompletedEvent +from .beta_response_reasoning_text_done_event import BetaResponseReasoningTextDoneEvent +from .beta_response_mcp_call_in_progress_event import BetaResponseMcpCallInProgressEvent +from .beta_response_reasoning_text_delta_event import BetaResponseReasoningTextDeltaEvent +from .beta_response_audio_transcript_done_event import BetaResponseAudioTranscriptDoneEvent +from .beta_response_mcp_list_tools_failed_event import BetaResponseMcpListToolsFailedEvent +from .beta_response_audio_transcript_delta_event import BetaResponseAudioTranscriptDeltaEvent +from .beta_response_mcp_call_arguments_done_event import BetaResponseMcpCallArgumentsDoneEvent +from .beta_response_image_gen_call_completed_event import BetaResponseImageGenCallCompletedEvent +from .beta_response_mcp_call_arguments_delta_event import BetaResponseMcpCallArgumentsDeltaEvent +from .beta_response_mcp_list_tools_completed_event import BetaResponseMcpListToolsCompletedEvent +from .beta_response_image_gen_call_generating_event import BetaResponseImageGenCallGeneratingEvent +from .beta_response_web_search_call_completed_event import BetaResponseWebSearchCallCompletedEvent +from .beta_response_web_search_call_searching_event import BetaResponseWebSearchCallSearchingEvent +from .beta_response_file_search_call_completed_event import BetaResponseFileSearchCallCompletedEvent +from .beta_response_file_search_call_searching_event import BetaResponseFileSearchCallSearchingEvent +from .beta_response_image_gen_call_in_progress_event import BetaResponseImageGenCallInProgressEvent +from .beta_response_mcp_list_tools_in_progress_event import BetaResponseMcpListToolsInProgressEvent +from .beta_response_custom_tool_call_input_done_event import BetaResponseCustomToolCallInputDoneEvent +from .beta_response_reasoning_summary_part_done_event import BetaResponseReasoningSummaryPartDoneEvent +from .beta_response_reasoning_summary_text_done_event import BetaResponseReasoningSummaryTextDoneEvent +from .beta_response_web_search_call_in_progress_event import BetaResponseWebSearchCallInProgressEvent +from .beta_response_custom_tool_call_input_delta_event import BetaResponseCustomToolCallInputDeltaEvent +from .beta_response_file_search_call_in_progress_event import BetaResponseFileSearchCallInProgressEvent +from .beta_response_function_call_arguments_done_event import BetaResponseFunctionCallArgumentsDoneEvent +from .beta_response_image_gen_call_partial_image_event import BetaResponseImageGenCallPartialImageEvent +from .beta_response_output_text_annotation_added_event import BetaResponseOutputTextAnnotationAddedEvent +from .beta_response_reasoning_summary_part_added_event import BetaResponseReasoningSummaryPartAddedEvent +from .beta_response_reasoning_summary_text_delta_event import BetaResponseReasoningSummaryTextDeltaEvent +from .beta_response_function_call_arguments_delta_event import BetaResponseFunctionCallArgumentsDeltaEvent +from .beta_response_code_interpreter_call_code_done_event import BetaResponseCodeInterpreterCallCodeDoneEvent +from .beta_response_code_interpreter_call_completed_event import BetaResponseCodeInterpreterCallCompletedEvent +from .beta_response_code_interpreter_call_code_delta_event import BetaResponseCodeInterpreterCallCodeDeltaEvent +from .beta_response_code_interpreter_call_in_progress_event import BetaResponseCodeInterpreterCallInProgressEvent +from .beta_response_code_interpreter_call_interpreting_event import BetaResponseCodeInterpreterCallInterpretingEvent + +__all__ = ["BetaResponsesServerEvent"] + +BetaResponsesServerEvent: TypeAlias = Annotated[ + Union[ + BetaResponseAudioDeltaEvent, + BetaResponseAudioDoneEvent, + BetaResponseAudioTranscriptDeltaEvent, + BetaResponseAudioTranscriptDoneEvent, + BetaResponseCodeInterpreterCallCodeDeltaEvent, + BetaResponseCodeInterpreterCallCodeDoneEvent, + BetaResponseCodeInterpreterCallCompletedEvent, + BetaResponseCodeInterpreterCallInProgressEvent, + BetaResponseCodeInterpreterCallInterpretingEvent, + BetaResponseCompletedEvent, + BetaResponseContentPartAddedEvent, + BetaResponseContentPartDoneEvent, + BetaResponseCreatedEvent, + BetaResponseErrorEvent, + BetaResponseFileSearchCallCompletedEvent, + BetaResponseFileSearchCallInProgressEvent, + BetaResponseFileSearchCallSearchingEvent, + BetaResponseFunctionCallArgumentsDeltaEvent, + BetaResponseFunctionCallArgumentsDoneEvent, + BetaResponseInProgressEvent, + BetaResponseFailedEvent, + BetaResponseIncompleteEvent, + BetaResponseOutputItemAddedEvent, + BetaResponseOutputItemDoneEvent, + BetaResponseReasoningSummaryPartAddedEvent, + BetaResponseReasoningSummaryPartDoneEvent, + BetaResponseReasoningSummaryTextDeltaEvent, + BetaResponseReasoningSummaryTextDoneEvent, + BetaResponseReasoningTextDeltaEvent, + BetaResponseReasoningTextDoneEvent, + BetaResponseRefusalDeltaEvent, + BetaResponseRefusalDoneEvent, + BetaResponseTextDeltaEvent, + BetaResponseTextDoneEvent, + BetaResponseWebSearchCallCompletedEvent, + BetaResponseWebSearchCallInProgressEvent, + BetaResponseWebSearchCallSearchingEvent, + BetaResponseImageGenCallCompletedEvent, + BetaResponseImageGenCallGeneratingEvent, + BetaResponseImageGenCallInProgressEvent, + BetaResponseImageGenCallPartialImageEvent, + BetaResponseMcpCallArgumentsDeltaEvent, + BetaResponseMcpCallArgumentsDoneEvent, + BetaResponseMcpCallCompletedEvent, + BetaResponseMcpCallFailedEvent, + BetaResponseMcpCallInProgressEvent, + BetaResponseMcpListToolsCompletedEvent, + BetaResponseMcpListToolsFailedEvent, + BetaResponseMcpListToolsInProgressEvent, + BetaResponseOutputTextAnnotationAddedEvent, + BetaResponseQueuedEvent, + BetaResponseCustomToolCallInputDeltaEvent, + BetaResponseCustomToolCallInputDoneEvent, + BetaResponseInjectCreatedEvent, + BetaResponseInjectFailedEvent, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/beta_skill_reference.py b/src/openai/types/beta/beta_skill_reference.py new file mode 100644 index 0000000000..2d8d041e1d --- /dev/null +++ b/src/openai/types/beta/beta_skill_reference.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaSkillReference"] + + +class BetaSkillReference(BaseModel): + skill_id: str + """The ID of the referenced skill.""" + + type: Literal["skill_reference"] + """References a skill created with the /v1/skills endpoint.""" + + version: Optional[str] = None + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/src/openai/types/beta/beta_skill_reference_param.py b/src/openai/types/beta/beta_skill_reference_param.py new file mode 100644 index 0000000000..306377f2e6 --- /dev/null +++ b/src/openai/types/beta/beta_skill_reference_param.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaSkillReferenceParam"] + + +class BetaSkillReferenceParam(TypedDict, total=False): + skill_id: Required[str] + """The ID of the referenced skill.""" + + type: Required[Literal["skill_reference"]] + """References a skill created with the /v1/skills endpoint.""" + + version: str + """Optional skill version. Use a positive integer or 'latest'. Omit for default.""" diff --git a/src/openai/types/beta/beta_tool.py b/src/openai/types/beta/beta_tool.py new file mode 100644 index 0000000000..fcef43a13a --- /dev/null +++ b/src/openai/types/beta/beta_tool.py @@ -0,0 +1,374 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias + +from ..._utils import PropertyInfo +from ..._models import BaseModel +from .beta_custom_tool import BetaCustomTool +from .beta_computer_tool import BetaComputerTool +from .beta_function_tool import BetaFunctionTool +from .beta_namespace_tool import BetaNamespaceTool +from .beta_web_search_tool import BetaWebSearchTool +from .beta_apply_patch_tool import BetaApplyPatchTool +from .beta_file_search_tool import BetaFileSearchTool +from .beta_tool_search_tool import BetaToolSearchTool +from .beta_function_shell_tool import BetaFunctionShellTool +from .beta_web_search_preview_tool import BetaWebSearchPreviewTool +from .beta_computer_use_preview_tool import BetaComputerUsePreviewTool +from .beta_container_network_policy_disabled import BetaContainerNetworkPolicyDisabled +from .beta_container_network_policy_allowlist import BetaContainerNetworkPolicyAllowlist + +__all__ = [ + "BetaTool", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", + "CodeInterpreter", + "CodeInterpreterContainer", + "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", + "ImageGeneration", + "ImageGenerationInputImageMask", + "LocalShell", +] + + +class McpAllowedToolsMcpToolFilter(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[List[str], McpAllowedToolsMcpToolFilter, None] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(BaseModel): + """A filter object to specify which tools are allowed.""" + + read_only: Optional[bool] = None + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: Optional[List[str]] = None + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(BaseModel): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: Optional[McpRequireApprovalMcpToolApprovalFilterAlways] = None + """A filter object to specify which tools are allowed.""" + + never: Optional[McpRequireApprovalMcpToolApprovalFilterNever] = None + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"], None] + + +class Mcp(BaseModel): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + + server_label: str + """A label for this MCP server, used to identify it in tool calls.""" + + type: Literal["mcp"] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] = None + """List of allowed tool names or a filter object.""" + + authorization: Optional[str] = None + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: Optional[bool] = None + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] = None + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] = None + """Specify which of the MCP server's tools require approval.""" + + server_description: Optional[str] = None + """Optional description of the MCP server, used to provide more context.""" + + server_url: Optional[str] = None + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: Optional[str] = None + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Annotated[ + Union[BetaContainerNetworkPolicyDisabled, BetaContainerNetworkPolicyAllowlist], PropertyInfo(discriminator="type") +] + + +class CodeInterpreterContainerCodeInterpreterToolAuto(BaseModel): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + + type: Literal["auto"] + """Always `auto`.""" + + file_ids: Optional[List[str]] = None + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] = None + """The memory limit for the code interpreter container.""" + + network_policy: Optional[CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy] = None + """Network access policy for the container.""" + + +CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] + + +class CodeInterpreter(BaseModel): + """A tool that runs Python code to help generate a response to a prompt.""" + + container: CodeInterpreterContainer + """The code interpreter container. + + Can be a container ID or an object that specifies uploaded file IDs to make + available to your code, along with an optional `memory_limit` setting. + """ + + type: Literal["code_interpreter"] + """The type of the code interpreter tool. Always `code_interpreter`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(BaseModel): + type: Literal["programmatic_tool_calling"] + """The type of the tool. Always `programmatic_tool_calling`.""" + + +class ImageGenerationInputImageMask(BaseModel): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + + file_id: Optional[str] = None + """File ID for the mask image.""" + + image_url: Optional[str] = None + """Base64-encoded mask image.""" + + +class ImageGeneration(BaseModel): + """A tool that generates images using the GPT image models.""" + + type: Literal["image_generation"] + """The type of the image generation tool. Always `image_generation`.""" + + action: Optional[Literal["generate", "edit", "auto"]] = None + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + + background: Optional[Literal["transparent", "opaque", "auto"]] = None + """ + Allows to set transparency for the background of the generated image(s). This + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + """ + + input_fidelity: Optional[Literal["high", "low"]] = None + """ + Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + """ + + input_image_mask: Optional[ImageGenerationInputImageMask] = None + """Optional mask for inpainting. + + Contains `image_url` (string, optional) and `file_id` (string, optional). + """ + + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + None, + ] = None + """The image generation model to use. Default: `gpt-image-1`.""" + + moderation: Optional[Literal["auto", "low"]] = None + """Moderation level for the generated image. Default: `auto`.""" + + output_compression: Optional[int] = None + """Compression level for the output image. Default: 100.""" + + output_format: Optional[Literal["png", "webp", "jpeg"]] = None + """The output format of the generated image. + + One of `png`, `webp`, or `jpeg`. Default: `png`. + """ + + partial_images: Optional[int] = None + """ + Number of partial images to generate in streaming mode, from 0 (default value) + to 3. + """ + + quality: Optional[Literal["low", "medium", "high", "auto"]] = None + """The quality of the generated image. + + One of `low`, `medium`, `high`, or `auto`. Default: `auto`. + """ + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"], None] = None + """The size of the generated images. + + For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are + supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height + must both be divisible by 16 and the requested aspect ratio must be between 1:3 + and 3:1. Resolutions above `2560x1440` are experimental, and the maximum + supported resolution is `3840x2160`. The requested size must also satisfy the + model's current pixel and edge limits. The standard sizes `1024x1024`, + `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is + supported for models that allow automatic sizing. For `dall-e-2`, use one of + `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, + `1792x1024`, or `1024x1792`. + """ + + +class LocalShell(BaseModel): + """A tool that allows the model to execute shell commands in a local environment.""" + + type: Literal["local_shell"] + """The type of the local shell tool. Always `local_shell`.""" + + +BetaTool: TypeAlias = Annotated[ + Union[ + BetaFunctionTool, + BetaFileSearchTool, + BetaComputerTool, + BetaComputerUsePreviewTool, + BetaWebSearchTool, + Mcp, + CodeInterpreter, + ProgrammaticToolCalling, + ImageGeneration, + LocalShell, + BetaFunctionShellTool, + BetaCustomTool, + BetaNamespaceTool, + BetaToolSearchTool, + BetaWebSearchPreviewTool, + BetaApplyPatchTool, + ], + PropertyInfo(discriminator="type"), +] diff --git a/src/openai/types/beta/beta_tool_choice_allowed.py b/src/openai/types/beta/beta_tool_choice_allowed.py new file mode 100644 index 0000000000..9b6f66b7c0 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_allowed.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceAllowed"] + + +class BetaToolChoiceAllowed(BaseModel): + """Constrains the tools available to the model to a pre-defined set.""" + + mode: Literal["auto", "required"] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: List[Dict[str, object]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Literal["allowed_tools"] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/src/openai/types/beta/beta_tool_choice_allowed_param.py b/src/openai/types/beta/beta_tool_choice_allowed_param.py new file mode 100644 index 0000000000..ed2b91e357 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_allowed_param.py @@ -0,0 +1,38 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceAllowedParam"] + + +class BetaToolChoiceAllowedParam(TypedDict, total=False): + """Constrains the tools available to the model to a pre-defined set.""" + + mode: Required[Literal["auto", "required"]] + """Constrains the tools available to the model to a pre-defined set. + + `auto` allows the model to pick from among the allowed tools and generate a + message. + + `required` requires the model to call one or more of the allowed tools. + """ + + tools: Required[Iterable[Dict[str, object]]] + """A list of tool definitions that the model should be allowed to call. + + For the Responses API, the list of tool definitions might look like: + + ```json + [ + { "type": "function", "name": "get_weather" }, + { "type": "mcp", "server_label": "deepwiki" }, + { "type": "image_generation" } + ] + ``` + """ + + type: Required[Literal["allowed_tools"]] + """Allowed tool configuration type. Always `allowed_tools`.""" diff --git a/src/openai/types/beta/beta_tool_choice_apply_patch.py b/src/openai/types/beta/beta_tool_choice_apply_patch.py new file mode 100644 index 0000000000..76bc9fe1f2 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_apply_patch.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceApplyPatch"] + + +class BetaToolChoiceApplyPatch(BaseModel): + """Forces the model to call the apply_patch tool when executing a tool call.""" + + type: Literal["apply_patch"] + """The tool to call. Always `apply_patch`.""" diff --git a/src/openai/types/beta/beta_tool_choice_apply_patch_param.py b/src/openai/types/beta/beta_tool_choice_apply_patch_param.py new file mode 100644 index 0000000000..4daf2c4a9e --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_apply_patch_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceApplyPatchParam"] + + +class BetaToolChoiceApplyPatchParam(TypedDict, total=False): + """Forces the model to call the apply_patch tool when executing a tool call.""" + + type: Required[Literal["apply_patch"]] + """The tool to call. Always `apply_patch`.""" diff --git a/src/openai/types/beta/beta_tool_choice_custom.py b/src/openai/types/beta/beta_tool_choice_custom.py new file mode 100644 index 0000000000..00a2f86150 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_custom.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceCustom"] + + +class BetaToolChoiceCustom(BaseModel): + """Use this option to force the model to call a specific custom tool.""" + + name: str + """The name of the custom tool to call.""" + + type: Literal["custom"] + """For custom tool calling, the type is always `custom`.""" diff --git a/src/openai/types/beta/beta_tool_choice_custom_param.py b/src/openai/types/beta/beta_tool_choice_custom_param.py new file mode 100644 index 0000000000..11ae09576a --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_custom_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceCustomParam"] + + +class BetaToolChoiceCustomParam(TypedDict, total=False): + """Use this option to force the model to call a specific custom tool.""" + + name: Required[str] + """The name of the custom tool to call.""" + + type: Required[Literal["custom"]] + """For custom tool calling, the type is always `custom`.""" diff --git a/src/openai/types/beta/beta_tool_choice_function.py b/src/openai/types/beta/beta_tool_choice_function.py new file mode 100644 index 0000000000..658737d6f7 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_function.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceFunction"] + + +class BetaToolChoiceFunction(BaseModel): + """Use this option to force the model to call a specific function.""" + + name: str + """The name of the function to call.""" + + type: Literal["function"] + """For function calling, the type is always `function`.""" diff --git a/src/openai/types/beta/beta_tool_choice_function_param.py b/src/openai/types/beta/beta_tool_choice_function_param.py new file mode 100644 index 0000000000..44dca10a5c --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_function_param.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceFunctionParam"] + + +class BetaToolChoiceFunctionParam(TypedDict, total=False): + """Use this option to force the model to call a specific function.""" + + name: Required[str] + """The name of the function to call.""" + + type: Required[Literal["function"]] + """For function calling, the type is always `function`.""" diff --git a/src/openai/types/beta/beta_tool_choice_mcp.py b/src/openai/types/beta/beta_tool_choice_mcp.py new file mode 100644 index 0000000000..659de2c4bd --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_mcp.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceMcp"] + + +class BetaToolChoiceMcp(BaseModel): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + + server_label: str + """The label of the MCP server to use.""" + + type: Literal["mcp"] + """For MCP tools, the type is always `mcp`.""" + + name: Optional[str] = None + """The name of the tool to call on the server.""" diff --git a/src/openai/types/beta/beta_tool_choice_mcp_param.py b/src/openai/types/beta/beta_tool_choice_mcp_param.py new file mode 100644 index 0000000000..7496c79dc4 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_mcp_param.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceMcpParam"] + + +class BetaToolChoiceMcpParam(TypedDict, total=False): + """ + Use this option to force the model to call a specific tool on a remote MCP server. + """ + + server_label: Required[str] + """The label of the MCP server to use.""" + + type: Required[Literal["mcp"]] + """For MCP tools, the type is always `mcp`.""" + + name: Optional[str] + """The name of the tool to call on the server.""" diff --git a/src/openai/types/beta/beta_tool_choice_options.py b/src/openai/types/beta/beta_tool_choice_options.py new file mode 100644 index 0000000000..847ffcdaf4 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_options.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BetaToolChoiceOptions"] + +BetaToolChoiceOptions: TypeAlias = Literal["none", "auto", "required"] diff --git a/src/openai/types/beta/beta_tool_choice_shell.py b/src/openai/types/beta/beta_tool_choice_shell.py new file mode 100644 index 0000000000..53e2bd7479 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_shell.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceShell"] + + +class BetaToolChoiceShell(BaseModel): + """Forces the model to call the shell tool when a tool call is required.""" + + type: Literal["shell"] + """The tool to call. Always `shell`.""" diff --git a/src/openai/types/beta/beta_tool_choice_shell_param.py b/src/openai/types/beta/beta_tool_choice_shell_param.py new file mode 100644 index 0000000000..20980de167 --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_shell_param.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceShellParam"] + + +class BetaToolChoiceShellParam(TypedDict, total=False): + """Forces the model to call the shell tool when a tool call is required.""" + + type: Required[Literal["shell"]] + """The tool to call. Always `shell`.""" diff --git a/src/openai/types/beta/beta_tool_choice_types.py b/src/openai/types/beta/beta_tool_choice_types.py new file mode 100644 index 0000000000..278f5193ae --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_types.py @@ -0,0 +1,40 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolChoiceTypes"] + + +class BetaToolChoiceTypes(BaseModel): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools). + """ + + type: Literal[ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter", + ] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer` + - `computer_use_preview` + - `computer_use` + - `code_interpreter` + - `image_generation` + """ diff --git a/src/openai/types/beta/beta_tool_choice_types_param.py b/src/openai/types/beta/beta_tool_choice_types_param.py new file mode 100644 index 0000000000..b23057bddf --- /dev/null +++ b/src/openai/types/beta/beta_tool_choice_types_param.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolChoiceTypesParam"] + + +class BetaToolChoiceTypesParam(TypedDict, total=False): + """ + Indicates that the model should use a built-in tool to generate a response. + [Learn more about built-in tools](https://platform.openai.com/docs/guides/tools). + """ + + type: Required[ + Literal[ + "file_search", + "web_search_preview", + "computer", + "computer_use_preview", + "computer_use", + "web_search_preview_2025_03_11", + "image_generation", + "code_interpreter", + ] + ] + """The type of hosted tool the model should to use. + + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + + Allowed values are: + + - `file_search` + - `web_search_preview` + - `computer` + - `computer_use_preview` + - `computer_use` + - `code_interpreter` + - `image_generation` + """ diff --git a/src/openai/types/beta/beta_tool_param.py b/src/openai/types/beta/beta_tool_param.py new file mode 100644 index 0000000000..594084e2db --- /dev/null +++ b/src/openai/types/beta/beta_tool_param.py @@ -0,0 +1,369 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from .beta_custom_tool_param import BetaCustomToolParam +from .beta_computer_tool_param import BetaComputerToolParam +from .beta_function_tool_param import BetaFunctionToolParam +from .beta_namespace_tool_param import BetaNamespaceToolParam +from .beta_web_search_tool_param import BetaWebSearchToolParam +from .beta_apply_patch_tool_param import BetaApplyPatchToolParam +from .beta_file_search_tool_param import BetaFileSearchToolParam +from .beta_tool_search_tool_param import BetaToolSearchToolParam +from .beta_function_shell_tool_param import BetaFunctionShellToolParam +from .beta_web_search_preview_tool_param import BetaWebSearchPreviewToolParam +from .beta_computer_use_preview_tool_param import BetaComputerUsePreviewToolParam +from .beta_container_network_policy_disabled_param import BetaContainerNetworkPolicyDisabledParam +from .beta_container_network_policy_allowlist_param import BetaContainerNetworkPolicyAllowlistParam + +__all__ = [ + "BetaToolParam", + "Mcp", + "McpAllowedTools", + "McpAllowedToolsMcpToolFilter", + "McpRequireApproval", + "McpRequireApprovalMcpToolApprovalFilter", + "McpRequireApprovalMcpToolApprovalFilterAlways", + "McpRequireApprovalMcpToolApprovalFilterNever", + "CodeInterpreter", + "CodeInterpreterContainer", + "CodeInterpreterContainerCodeInterpreterToolAuto", + "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", + "ImageGeneration", + "ImageGenerationInputImageMask", + "LocalShell", +] + + +class McpAllowedToolsMcpToolFilter(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +McpAllowedTools: TypeAlias = Union[SequenceNotStr[str], McpAllowedToolsMcpToolFilter] + + +class McpRequireApprovalMcpToolApprovalFilterAlways(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilterNever(TypedDict, total=False): + """A filter object to specify which tools are allowed.""" + + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. + + If an MCP server is + [annotated with `readOnlyHint`](https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint), + it will match this filter. + """ + + tool_names: SequenceNotStr[str] + """List of allowed tool names.""" + + +class McpRequireApprovalMcpToolApprovalFilter(TypedDict, total=False): + """Specify which of the MCP server's tools require approval. + + Can be + `always`, `never`, or a filter object associated with tools + that require approval. + """ + + always: McpRequireApprovalMcpToolApprovalFilterAlways + """A filter object to specify which tools are allowed.""" + + never: McpRequireApprovalMcpToolApprovalFilterNever + """A filter object to specify which tools are allowed.""" + + +McpRequireApproval: TypeAlias = Union[McpRequireApprovalMcpToolApprovalFilter, Literal["always", "never"]] + + +class Mcp(TypedDict, total=False): + """ + Give the model access to additional tools via remote Model Context Protocol + (MCP) servers. [Learn more about MCP](https://platform.openai.com/docs/guides/tools-remote-mcp). + """ + + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls.""" + + type: Required[Literal["mcp"]] + """The type of the MCP tool. Always `mcp`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + allowed_tools: Optional[McpAllowedTools] + """List of allowed tool names or a filter object.""" + + authorization: str + """ + An OAuth access token that can be used with a remote MCP server, either with a + custom MCP server URL or a service connector. Your application must handle the + OAuth authorization flow and provide the token here. + """ + + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. Learn more + about service connectors + [here](https://platform.openai.com/docs/guides/tools-remote-mcp#connectors). + + Currently supported `connector_id` values are: + + - Dropbox: `connector_dropbox` + - Gmail: `connector_gmail` + - Google Calendar: `connector_googlecalendar` + - Google Drive: `connector_googledrive` + - Microsoft Teams: `connector_microsoftteams` + - Outlook Calendar: `connector_outlookcalendar` + - Outlook Email: `connector_outlookemail` + - SharePoint: `connector_sharepoint` + """ + + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + + headers: Optional[Dict[str, str]] + """Optional HTTP headers to send to the MCP server. + + Use for authentication or other purposes. + """ + + require_approval: Optional[McpRequireApproval] + """Specify which of the MCP server's tools require approval.""" + + server_description: str + """Optional description of the MCP server, used to provide more context.""" + + server_url: str + """The URL for the MCP server. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. + + One of `server_url`, `connector_id`, or `tunnel_id` must be provided. + """ + + +CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy: TypeAlias = Union[ + BetaContainerNetworkPolicyDisabledParam, BetaContainerNetworkPolicyAllowlistParam +] + + +class CodeInterpreterContainerCodeInterpreterToolAuto(TypedDict, total=False): + """Configuration for a code interpreter container. + + Optionally specify the IDs of the files to run the code on. + """ + + type: Required[Literal["auto"]] + """Always `auto`.""" + + file_ids: SequenceNotStr[str] + """An optional list of uploaded files to make available to your code.""" + + memory_limit: Optional[Literal["1g", "4g", "16g", "64g"]] + """The memory limit for the code interpreter container.""" + + network_policy: CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy + """Network access policy for the container.""" + + +CodeInterpreterContainer: TypeAlias = Union[str, CodeInterpreterContainerCodeInterpreterToolAuto] + + +class CodeInterpreter(TypedDict, total=False): + """A tool that runs Python code to help generate a response to a prompt.""" + + container: Required[CodeInterpreterContainer] + """The code interpreter container. + + Can be a container ID or an object that specifies uploaded file IDs to make + available to your code, along with an optional `memory_limit` setting. + """ + + type: Required[Literal["code_interpreter"]] + """The type of the code interpreter tool. Always `code_interpreter`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The type of the tool. Always `programmatic_tool_calling`.""" + + +class ImageGenerationInputImageMask(TypedDict, total=False): + """Optional mask for inpainting. + + Contains `image_url` + (string, optional) and `file_id` (string, optional). + """ + + file_id: str + """File ID for the mask image.""" + + image_url: str + """Base64-encoded mask image.""" + + +class ImageGeneration(TypedDict, total=False): + """A tool that generates images using the GPT image models.""" + + type: Required[Literal["image_generation"]] + """The type of the image generation tool. Always `image_generation`.""" + + action: Literal["generate", "edit", "auto"] + """Whether to generate a new image or edit an existing image. Default: `auto`.""" + + background: Literal["transparent", "opaque", "auto"] + """ + Allows to set transparency for the background of the generated image(s). This + parameter is only supported for GPT image models that support transparent + backgrounds. Must be one of `transparent`, `opaque`, or `auto` (default value). + When `auto` is used, the model will automatically determine the best background + for the image. + + `gpt-image-2` and `gpt-image-2-2026-04-21` do not support transparent + backgrounds. Requests with `background` set to `transparent` will return an + error for these models; use `opaque` or `auto` instead. + + If `transparent`, the output format needs to support transparency, so it should + be set to either `png` (default value) or `webp`. + """ + + input_fidelity: Optional[Literal["high", "low"]] + """ + Control how much effort the model will exert to match the style and features, + especially facial features, of input images. This parameter is only supported + for `gpt-image-1` and `gpt-image-1.5` and later models, unsupported for + `gpt-image-1-mini`. Supports `high` and `low`. Defaults to `low`. + """ + + input_image_mask: ImageGenerationInputImageMask + """Optional mask for inpainting. + + Contains `image_url` (string, optional) and `file_id` (string, optional). + """ + + model: Union[ + str, + Literal[ + "gpt-image-1", + "gpt-image-1-mini", + "gpt-image-2", + "gpt-image-2-2026-04-21", + "gpt-image-1.5", + "chatgpt-image-latest", + ], + ] + """The image generation model to use. Default: `gpt-image-1`.""" + + moderation: Literal["auto", "low"] + """Moderation level for the generated image. Default: `auto`.""" + + output_compression: int + """Compression level for the output image. Default: 100.""" + + output_format: Literal["png", "webp", "jpeg"] + """The output format of the generated image. + + One of `png`, `webp`, or `jpeg`. Default: `png`. + """ + + partial_images: int + """ + Number of partial images to generate in streaming mode, from 0 (default value) + to 3. + """ + + quality: Literal["low", "medium", "high", "auto"] + """The quality of the generated image. + + One of `low`, `medium`, `high`, or `auto`. Default: `auto`. + """ + + size: Union[str, Literal["1024x1024", "1024x1536", "1536x1024", "auto"]] + """The size of the generated images. + + For `gpt-image-2` and `gpt-image-2-2026-04-21`, arbitrary resolutions are + supported as `WIDTHxHEIGHT` strings, for example `1536x864`. Width and height + must both be divisible by 16 and the requested aspect ratio must be between 1:3 + and 3:1. Resolutions above `2560x1440` are experimental, and the maximum + supported resolution is `3840x2160`. The requested size must also satisfy the + model's current pixel and edge limits. The standard sizes `1024x1024`, + `1536x1024`, and `1024x1536` are supported by the GPT image models; `auto` is + supported for models that allow automatic sizing. For `dall-e-2`, use one of + `256x256`, `512x512`, or `1024x1024`. For `dall-e-3`, use one of `1024x1024`, + `1792x1024`, or `1024x1792`. + """ + + +class LocalShell(TypedDict, total=False): + """A tool that allows the model to execute shell commands in a local environment.""" + + type: Required[Literal["local_shell"]] + """The type of the local shell tool. Always `local_shell`.""" + + +BetaToolParam: TypeAlias = Union[ + BetaFunctionToolParam, + BetaFileSearchToolParam, + BetaComputerToolParam, + BetaComputerUsePreviewToolParam, + BetaWebSearchToolParam, + Mcp, + CodeInterpreter, + ProgrammaticToolCalling, + ImageGeneration, + LocalShell, + BetaFunctionShellToolParam, + BetaCustomToolParam, + BetaNamespaceToolParam, + BetaToolSearchToolParam, + BetaWebSearchPreviewToolParam, + BetaApplyPatchToolParam, +] diff --git a/src/openai/types/beta/beta_tool_search_tool.py b/src/openai/types/beta/beta_tool_search_tool.py new file mode 100644 index 0000000000..700aad0285 --- /dev/null +++ b/src/openai/types/beta/beta_tool_search_tool.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaToolSearchTool"] + + +class BetaToolSearchTool(BaseModel): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Literal["tool_search"] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] = None + """Description shown to the model for a client-executed tool search tool.""" + + execution: Optional[Literal["server", "client"]] = None + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] = None + """Parameter schema for a client-executed tool search tool.""" diff --git a/src/openai/types/beta/beta_tool_search_tool_param.py b/src/openai/types/beta/beta_tool_search_tool_param.py new file mode 100644 index 0000000000..046445307e --- /dev/null +++ b/src/openai/types/beta/beta_tool_search_tool_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaToolSearchToolParam"] + + +class BetaToolSearchToolParam(TypedDict, total=False): + """Hosted or BYOT tool search configuration for deferred tools.""" + + type: Required[Literal["tool_search"]] + """The type of the tool. Always `tool_search`.""" + + description: Optional[str] + """Description shown to the model for a client-executed tool search tool.""" + + execution: Literal["server", "client"] + """Whether tool search is executed by the server or by the client.""" + + parameters: Optional[object] + """Parameter schema for a client-executed tool search tool.""" diff --git a/src/openai/types/beta/beta_web_search_preview_tool.py b/src/openai/types/beta/beta_web_search_preview_tool.py new file mode 100644 index 0000000000..fa8fe73e55 --- /dev/null +++ b/src/openai/types/beta/beta_web_search_preview_tool.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaWebSearchPreviewTool", "UserLocation"] + + +class UserLocation(BaseModel): + """The user's location.""" + + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class BetaWebSearchPreviewTool(BaseModel): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + + type: Literal["web_search_preview", "web_search_preview_2025_03_11"] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_content_types: Optional[List[Literal["text", "image"]]] = None + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None + """The user's location.""" diff --git a/src/openai/types/beta/beta_web_search_preview_tool_param.py b/src/openai/types/beta/beta_web_search_preview_tool_param.py new file mode 100644 index 0000000000..f2ffb7e3ce --- /dev/null +++ b/src/openai/types/beta/beta_web_search_preview_tool_param.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["BetaWebSearchPreviewToolParam", "UserLocation"] + + +class UserLocation(TypedDict, total=False): + """The user's location.""" + + type: Required[Literal["approximate"]] + """The type of location approximation. Always `approximate`.""" + + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + +class BetaWebSearchPreviewToolParam(TypedDict, total=False): + """This tool searches the web for relevant results to use in a response. + + Learn more about the [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + + type: Required[Literal["web_search_preview", "web_search_preview_2025_03_11"]] + """The type of the web search tool. + + One of `web_search_preview` or `web_search_preview_2025_03_11`. + """ + + search_content_types: List[Literal["text", "image"]] + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] + """The user's location.""" diff --git a/src/openai/types/beta/beta_web_search_tool.py b/src/openai/types/beta/beta_web_search_tool.py new file mode 100644 index 0000000000..3c44ef75dc --- /dev/null +++ b/src/openai/types/beta/beta_web_search_tool.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BetaWebSearchTool", "Filters", "UserLocation"] + + +class Filters(BaseModel): + """Filters for the search.""" + + allowed_domains: Optional[List[str]] = None + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class UserLocation(BaseModel): + """The approximate location of the user.""" + + city: Optional[str] = None + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] = None + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] = None + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] = None + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Optional[Literal["approximate"]] = None + """The type of location approximation. Always `approximate`.""" + + +class BetaWebSearchTool(BaseModel): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + + type: Literal["web_search", "web_search_2025_08_26"] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + filters: Optional[Filters] = None + """Filters for the search.""" + + search_context_size: Optional[Literal["low", "medium", "high"]] = None + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] = None + """The approximate location of the user.""" diff --git a/src/openai/types/beta/beta_web_search_tool_param.py b/src/openai/types/beta/beta_web_search_tool_param.py new file mode 100644 index 0000000000..0a99714620 --- /dev/null +++ b/src/openai/types/beta/beta_web_search_tool_param.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Literal, Required, TypedDict + +from ..._types import SequenceNotStr + +__all__ = ["BetaWebSearchToolParam", "Filters", "UserLocation"] + + +class Filters(TypedDict, total=False): + """Filters for the search.""" + + allowed_domains: Optional[SequenceNotStr[str]] + """Allowed domains for the search. + + If not provided, all domains are allowed. Subdomains of the provided domains are + allowed as well. + + Example: `["pubmed.ncbi.nlm.nih.gov"]` + """ + + +class UserLocation(TypedDict, total=False): + """The approximate location of the user.""" + + city: Optional[str] + """Free text input for the city of the user, e.g. `San Francisco`.""" + + country: Optional[str] + """ + The two-letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of + the user, e.g. `US`. + """ + + region: Optional[str] + """Free text input for the region of the user, e.g. `California`.""" + + timezone: Optional[str] + """ + The [IANA timezone](https://timeapi.io/documentation/iana-timezones) of the + user, e.g. `America/Los_Angeles`. + """ + + type: Literal["approximate"] + """The type of location approximation. Always `approximate`.""" + + +class BetaWebSearchToolParam(TypedDict, total=False): + """Search the Internet for sources related to the prompt. + + Learn more about the + [web search tool](https://platform.openai.com/docs/guides/tools-web-search). + """ + + type: Required[Literal["web_search", "web_search_2025_08_26"]] + """The type of the web search tool. + + One of `web_search` or `web_search_2025_08_26`. + """ + + filters: Optional[Filters] + """Filters for the search.""" + + search_context_size: Literal["low", "medium", "high"] + """High level guidance for the amount of context window space to use for the + search. + + One of `low`, `medium`, or `high`. `medium` is the default. + """ + + user_location: Optional[UserLocation] + """The approximate location of the user.""" diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py new file mode 100644 index 0000000000..8e8a821521 --- /dev/null +++ b/src/openai/types/beta/response_compact_params.py @@ -0,0 +1,192 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo +from .beta_response_input_item_param import BetaResponseInputItemParam + +__all__ = ["ResponseCompactParams", "PromptCacheOptions"] + + +class ResponseCompactParams(TypedDict, total=False): + model: Required[ + Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + None, + ] + ] + """Model ID used to generate the response, like `gpt-5` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + input: Union[str, Iterable[BetaResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt_cache_key: Optional[str] + """A key to use when reading from or writing to the prompt cache.""" + + prompt_cache_options: Optional[PromptCacheOptions] + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """How long to retain a prompt cache entry created by this request.""" + + service_tier: Optional[Literal["auto", "default", "flex", "priority"]] + """The service tier to use for this request.""" + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py new file mode 100644 index 0000000000..35694e319a --- /dev/null +++ b/src/openai/types/beta/response_create_params.py @@ -0,0 +1,621 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo +from .beta_tool_param import BetaToolParam +from .beta_response_includable import BetaResponseIncludable +from .beta_tool_choice_options import BetaToolChoiceOptions +from .beta_response_input_param import BetaResponseInputParam +from .beta_response_prompt_param import BetaResponsePromptParam +from .beta_tool_choice_mcp_param import BetaToolChoiceMcpParam +from .beta_tool_choice_shell_param import BetaToolChoiceShellParam +from .beta_tool_choice_types_param import BetaToolChoiceTypesParam +from .beta_tool_choice_custom_param import BetaToolChoiceCustomParam +from .beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam +from .beta_response_text_config_param import BetaResponseTextConfigParam +from .beta_tool_choice_function_param import BetaToolChoiceFunctionParam +from .beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam +from .beta_response_conversation_param_param import BetaResponseConversationParamParam + +__all__ = [ + "ResponseCreateParamsBase", + "ContextManagement", + "Conversation", + "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "MultiAgent", + "PromptCacheOptions", + "Reasoning", + "StreamOptions", + "ToolChoice", + "ToolChoiceBetaSpecificProgrammaticToolCallingParam", + "ResponseCreateParamsNonStreaming", + "ResponseCreateParamsStreaming", +] + + +class ResponseCreateParamsBase(TypedDict, total=False): + background: Optional[bool] + """ + Whether to run the model response in the background. + [Learn more](https://platform.openai.com/docs/guides/background). + """ + + context_management: Optional[Iterable[ContextManagement]] + """Context management configuration for this request.""" + + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + include: Optional[List[BetaResponseIncludable]] + """Specify additional output data to include in the model response. + + Currently supported values are: + + - `web_search_call.action.sources`: Include the sources of the web search tool + call. + - `code_interpreter_call.outputs`: Includes the outputs of python code execution + in code interpreter tool call items. + - `computer_call_output.output.image_url`: Include image urls from the computer + call output. + - `file_search_call.results`: Include the search results of the file search tool + call. + - `message.input_image.image_url`: Include image urls from the input message. + - `message.output_text.logprobs`: Include logprobs with assistant messages. + - `reasoning.encrypted_content`: Includes an encrypted version of reasoning + tokens in reasoning item outputs. This enables reasoning items to be used in + multi-turn conversations when using the Responses API statelessly (like when + the `store` parameter is set to `false`, or when an organization is enrolled + in the zero data retention program). + """ + + input: Union[str, BetaResponseInputParam] + """Text, image, or file inputs to the model, used to generate a response. + + Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Image inputs](https://platform.openai.com/docs/guides/images) + - [File inputs](https://platform.openai.com/docs/guides/pdf-files) + - [Conversation state](https://platform.openai.com/docs/guides/conversation-state) + - [Function calling](https://platform.openai.com/docs/guides/function-calling) + """ + + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. + + When using along with `previous_response_id`, the instructions from a previous + response will not be carried over to the next response. This makes it simple to + swap out system (or developer) messages in new responses. + """ + + max_output_tokens: Optional[int] + """ + An upper bound for the number of tokens that can be generated for a response, + including visible output tokens and + [reasoning tokens](https://platform.openai.com/docs/guides/reasoning). + """ + + max_tool_calls: Optional[int] + """ + The maximum number of total calls to built-in tools that can be processed in a + response. This maximum number applies across all built-in tool calls, not per + individual tool. Any further attempts to call a tool by the model will be + ignored. + """ + + metadata: Optional[Dict[str, str]] + """Set of 16 key-value pairs that can be attached to an object. + + This can be useful for storing additional information about the object in a + structured format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings with + a maximum length of 512 characters. + """ + + model: Union[ + Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.4-nano", + "gpt-5.4-mini-2026-03-17", + "gpt-5.4-nano-2026-03-17", + "gpt-5.3-chat-latest", + "gpt-5.2", + "gpt-5.2-2025-12-11", + "gpt-5.2-chat-latest", + "gpt-5.2-pro", + "gpt-5.2-pro-2025-12-11", + "gpt-5.1", + "gpt-5.1-2025-11-13", + "gpt-5.1-codex", + "gpt-5.1-mini", + "gpt-5.1-chat-latest", + "gpt-5", + "gpt-5-mini", + "gpt-5-nano", + "gpt-5-2025-08-07", + "gpt-5-mini-2025-08-07", + "gpt-5-nano-2025-08-07", + "gpt-5-chat-latest", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.1-2025-04-14", + "gpt-4.1-mini-2025-04-14", + "gpt-4.1-nano-2025-04-14", + "o4-mini", + "o4-mini-2025-04-16", + "o3", + "o3-2025-04-16", + "o3-mini", + "o3-mini-2025-01-31", + "o1", + "o1-2024-12-17", + "o1-preview", + "o1-preview-2024-09-12", + "o1-mini", + "o1-mini-2024-09-12", + "gpt-4o", + "gpt-4o-2024-11-20", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4o-audio-preview", + "gpt-4o-audio-preview-2024-10-01", + "gpt-4o-audio-preview-2024-12-17", + "gpt-4o-audio-preview-2025-06-03", + "gpt-4o-mini-audio-preview", + "gpt-4o-mini-audio-preview-2024-12-17", + "gpt-4o-search-preview", + "gpt-4o-mini-search-preview", + "gpt-4o-search-preview-2025-03-11", + "gpt-4o-mini-search-preview-2025-03-11", + "chatgpt-4o-latest", + "codex-mini-latest", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4-turbo", + "gpt-4-turbo-2024-04-09", + "gpt-4-0125-preview", + "gpt-4-turbo-preview", + "gpt-4-1106-preview", + "gpt-4-vision-preview", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo-0125", + "gpt-3.5-turbo-16k-0613", + "o1-pro", + "o1-pro-2025-03-19", + "o3-pro", + "o3-pro-2025-06-10", + "o3-deep-research", + "o3-deep-research-2025-06-26", + "o4-mini-deep-research", + "o4-mini-deep-research-2025-06-26", + "computer-use-preview", + "computer-use-preview-2025-03-11", + "gpt-5-codex", + "gpt-5-pro", + "gpt-5-pro-2025-10-06", + "gpt-5.1-codex-max", + ], + str, + ] + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + moderation: Optional[Moderation] + """Configuration for running moderation on the input and output of this response.""" + + multi_agent: Optional[MultiAgent] + """Configuration for server-hosted multi-agent execution.""" + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + prompt: Optional[BetaResponsePromptParam] + """ + Reference to a prompt template and its variables. + [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). + """ + + prompt_cache_key: str + """ + Used by OpenAI to cache responses for similar requests to optimize your cache + hit rates. Replaces the `user` field. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching). + """ + + prompt_cache_options: PromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] + """Deprecated. Use `prompt_cache_options.ttl` instead. + + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. + [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. + + For older models that support both `in_memory` and `24h`, the default depends on + your organization's data retention policy: + + - Organizations without ZDR enabled default to `24h`. + - Organizations with ZDR enabled default to `in_memory` when + `prompt_cache_retention` is not specified. + """ + + reasoning: Optional[Reasoning] + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + safety_identifier: str + """ + A stable identifier used to help detect users of your application that may be + violating OpenAI's usage policies. The IDs should be a string that uniquely + identifies each user, with a maximum length of 64 characters. We recommend + hashing their username or email address, in order to avoid sending us any + identifying information. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] + """Specifies the processing type used for serving the request. + + - If set to 'auto', then the request will be processed with the service tier + configured in the Project settings. Unless otherwise configured, the Project + will use 'default'. + - If set to 'default', then the request will be processed with the standard + pricing and performance for the selected model. + - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or + '[priority](https://openai.com/api-priority-processing/)', then the request + will be processed with the corresponding service tier. + - When not set, the default behavior is 'auto'. + + When the `service_tier` parameter is set, the response body will include the + `service_tier` value based on the processing mode actually used to serve the + request. This response value may be different from the value set in the + parameter. + """ + + store: Optional[bool] + """Whether to store the generated model response for later retrieval via API.""" + + stream_options: Optional[StreamOptions] + """Options for streaming responses. Only set this when you set `stream: true`.""" + + temperature: Optional[float] + """What sampling temperature to use, between 0 and 2. + + Higher values like 0.8 will make the output more random, while lower values like + 0.2 will make it more focused and deterministic. We generally recommend altering + this or `top_p` but not both. + """ + + text: BetaResponseTextConfigParam + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: ToolChoice + """ + How the model should select which tool (or tools) to use when generating a + response. See the `tools` parameter to see how to specify which tools the model + can call. + """ + + tools: Iterable[BetaToolParam] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + + We support the following categories of tools: + + - **Built-in tools**: Tools that are provided by OpenAI that extend the model's + capabilities, like + [web search](https://platform.openai.com/docs/guides/tools-web-search) or + [file search](https://platform.openai.com/docs/guides/tools-file-search). + Learn more about + [built-in tools](https://platform.openai.com/docs/guides/tools). + - **MCP Tools**: Integrations with third-party systems via custom MCP servers or + predefined connectors such as Google Drive and SharePoint. Learn more about + [MCP Tools](https://platform.openai.com/docs/guides/tools-connectors-mcp). + - **Function calls (custom tools)**: Functions that are defined by you, enabling + the model to call your own code with strongly typed arguments and outputs. + Learn more about + [function calling](https://platform.openai.com/docs/guides/function-calling). + You can also use custom tools to call your own code. + """ + + top_logprobs: Optional[int] + """ + An integer between 0 and 20 specifying the maximum number of most likely tokens + to return at each token position, each with an associated log probability. In + some cases, the number of returned tokens may be fewer than requested. + """ + + top_p: Optional[float] + """ + An alternative to sampling with temperature, called nucleus sampling, where the + model considers the results of the tokens with top_p probability mass. So 0.1 + means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + """ + + truncation: Optional[Literal["auto", "disabled"]] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. + - `disabled` (default): If the input size will exceed the context window size + for a model, the request will fail with a 400 error. + """ + + user: str + """This field is being replaced by `safety_identifier` and `prompt_cache_key`. + + Use `prompt_cache_key` instead to maintain caching optimizations. A stable + identifier for your end-users. Used to boost cache hit rates by better bucketing + similar requests and to help OpenAI detect and prevent abuse. + [Learn more](https://platform.openai.com/docs/guides/safety-best-practices#safety-identifiers). + """ + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +class ContextManagement(TypedDict, total=False): + type: Required[str] + """The context management entry type. Currently only 'compaction' is supported.""" + + compact_threshold: Optional[int] + """Token threshold at which compaction should be triggered for this entry.""" + + +Conversation: TypeAlias = Union[str, BetaResponseConversationParamParam] + + +class ModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] + """The moderation policy for the response output.""" + + +class Moderation(TypedDict, total=False): + """Configuration for running moderation on the input and output of this response.""" + + model: Required[str] + """The moderation model to use for moderated completions, e.g. + + 'omni-moderation-latest'. + """ + + policy: Optional[ModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class MultiAgent(TypedDict, total=False): + """Configuration for server-hosted multi-agent execution.""" + + enabled: Required[bool] + """Whether to enable server-hosted multi-agent execution for this response.""" + + max_concurrent_subagents: int + """ + `max_concurrent_subagents` sets the maximum number of subagents that can be + active simultaneously across the entire agent tree. It includes all + descendants—children, grandchildren, and deeper subagents—but excludes the root + agent. The API does not impose a fixed upper bound on this setting. The default + is `3`, which is recommended for most workloads. Multi-agent runs also have no + fixed limit on tree depth or the total number of subagents created during a run. + """ + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + + +class Reasoning(TypedDict, total=False): + """**gpt-5 and o-series models only** + + Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class StreamOptions(TypedDict, total=False): + """Options for streaming responses. Only set this when you set `stream: true`.""" + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + +class ToolChoiceBetaSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowedParam, + BetaToolChoiceTypesParam, + BetaToolChoiceFunctionParam, + BetaToolChoiceMcpParam, + BetaToolChoiceCustomParam, + ToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatchParam, + BetaToolChoiceShellParam, +] + + +class ResponseCreateParamsNonStreaming(ResponseCreateParamsBase, total=False): + stream: Optional[Literal[False]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + +class ResponseCreateParamsStreaming(ResponseCreateParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + +ResponseCreateParams = Union[ResponseCreateParamsNonStreaming, ResponseCreateParamsStreaming] diff --git a/src/openai/types/beta/response_retrieve_params.py b/src/openai/types/beta/response_retrieve_params.py new file mode 100644 index 0000000000..2afbcca766 --- /dev/null +++ b/src/openai/types/beta/response_retrieve_params.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo +from .beta_response_includable import BetaResponseIncludable + +__all__ = ["ResponseRetrieveParamsBase", "ResponseRetrieveParamsNonStreaming", "ResponseRetrieveParamsStreaming"] + + +class ResponseRetrieveParamsBase(TypedDict, total=False): + include: List[BetaResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ + + include_obfuscation: bool + """When true, stream obfuscation will be enabled. + + Stream obfuscation adds random characters to an `obfuscation` field on streaming + delta events to normalize payload sizes as a mitigation to certain side-channel + attacks. These obfuscation fields are included by default, but add a small + amount of overhead to the data stream. You can set `include_obfuscation` to + false to optimize for bandwidth if you trust the network links between your + application and the OpenAI API. + """ + + starting_after: int + """The sequence number of the event after which to start streaming.""" + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +class ResponseRetrieveParamsNonStreaming(ResponseRetrieveParamsBase, total=False): + stream: Literal[False] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + +class ResponseRetrieveParamsStreaming(ResponseRetrieveParamsBase): + stream: Required[Literal[True]] + """ + If set to true, the model response data will be streamed to the client as it is + generated using + [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). + See the + [Streaming section below](https://platform.openai.com/docs/api-reference/responses-streaming) + for more information. + """ + + +ResponseRetrieveParams = Union[ResponseRetrieveParamsNonStreaming, ResponseRetrieveParamsStreaming] diff --git a/src/openai/types/beta/responses/__init__.py b/src/openai/types/beta/responses/__init__.py new file mode 100644 index 0000000000..da68be9cc8 --- /dev/null +++ b/src/openai/types/beta/responses/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .input_item_list_params import InputItemListParams as InputItemListParams +from .beta_response_item_list import BetaResponseItemList as BetaResponseItemList +from .input_token_count_params import InputTokenCountParams as InputTokenCountParams +from .input_token_count_response import InputTokenCountResponse as InputTokenCountResponse diff --git a/src/openai/types/beta/responses/beta_response_item_list.py b/src/openai/types/beta/responses/beta_response_item_list.py new file mode 100644 index 0000000000..fcb24bc4f6 --- /dev/null +++ b/src/openai/types/beta/responses/beta_response_item_list.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ...._models import BaseModel +from ..beta_response_item import BetaResponseItem + +__all__ = ["BetaResponseItemList"] + + +class BetaResponseItemList(BaseModel): + """A list of Response items.""" + + data: List[BetaResponseItem] + """A list of items used to generate this response.""" + + first_id: str + """The ID of the first item in the list.""" + + has_more: bool + """Whether there are more items available.""" + + last_id: str + """The ID of the last item in the list.""" + + object: Literal["list"] + """The type of object returned, must be `list`.""" diff --git a/src/openai/types/beta/responses/input_item_list_params.py b/src/openai/types/beta/responses/input_item_list_params.py new file mode 100644 index 0000000000..998e90d516 --- /dev/null +++ b/src/openai/types/beta/responses/input_item_list_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Literal, Annotated, TypedDict + +from ...._utils import PropertyInfo +from ..beta_response_includable import BetaResponseIncludable + +__all__ = ["InputItemListParams"] + + +class InputItemListParams(TypedDict, total=False): + after: str + """An item ID to list items after, used in pagination.""" + + include: List[BetaResponseIncludable] + """Additional fields to include in the response. + + See the `include` parameter for Response creation above for more information. + """ + + limit: int + """A limit on the number of objects to be returned. + + Limit can range between 1 and 100, and the default is 20. + """ + + order: Literal["asc", "desc"] + """The order to return the input items in. Default is `desc`. + + - `asc`: Return the input items in ascending order. + - `desc`: Return the input items in descending order. + """ + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] diff --git a/src/openai/types/beta/responses/input_token_count_params.py b/src/openai/types/beta/responses/input_token_count_params.py new file mode 100644 index 0000000000..0d3c91e86d --- /dev/null +++ b/src/openai/types/beta/responses/input_token_count_params.py @@ -0,0 +1,216 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ...._utils import PropertyInfo +from ..beta_tool_param import BetaToolParam +from ..beta_tool_choice_options import BetaToolChoiceOptions +from ..beta_tool_choice_mcp_param import BetaToolChoiceMcpParam +from ..beta_tool_choice_shell_param import BetaToolChoiceShellParam +from ..beta_tool_choice_types_param import BetaToolChoiceTypesParam +from ..beta_tool_choice_custom_param import BetaToolChoiceCustomParam +from ..beta_response_input_item_param import BetaResponseInputItemParam +from ..beta_tool_choice_allowed_param import BetaToolChoiceAllowedParam +from ..beta_tool_choice_function_param import BetaToolChoiceFunctionParam +from ..beta_tool_choice_apply_patch_param import BetaToolChoiceApplyPatchParam +from ..beta_response_conversation_param_param import BetaResponseConversationParamParam +from ..beta_response_format_text_config_param import BetaResponseFormatTextConfigParam + +__all__ = [ + "InputTokenCountParams", + "Conversation", + "Reasoning", + "Text", + "ToolChoice", + "ToolChoiceBetaSpecificProgrammaticToolCallingParam", +] + + +class InputTokenCountParams(TypedDict, total=False): + conversation: Optional[Conversation] + """The conversation that this response belongs to. + + Items from this conversation are prepended to `input_items` for this response + request. Input items and output items from this response are automatically added + to this conversation after this response completes. + """ + + input: Union[str, Iterable[BetaResponseInputItemParam], None] + """Text, image, or file inputs to the model, used to generate a response""" + + instructions: Optional[str] + """ + A system (or developer) message inserted into the model's context. When used + along with `previous_response_id`, the instructions from a previous response + will not be carried over to the next response. This makes it simple to swap out + system (or developer) messages in new responses. + """ + + model: Optional[str] + """Model ID used to generate the response, like `gpt-4o` or `o3`. + + OpenAI offers a wide range of models with different capabilities, performance + characteristics, and price points. Refer to the + [model guide](https://platform.openai.com/docs/models) to browse and compare + available models. + """ + + parallel_tool_calls: Optional[bool] + """Whether to allow the model to run tool calls in parallel.""" + + personality: Union[str, Literal["friendly", "pragmatic"]] + """A model-owned style preset to apply to this request. + + Omit this parameter to use the model's default style. Supported values may + expand over time. Values must be at most 64 characters. + """ + + previous_response_id: Optional[str] + """The unique ID of the previous response to the model. + + Use this to create multi-turn conversations. Learn more about + [conversation state](https://platform.openai.com/docs/guides/conversation-state). + Cannot be used in conjunction with `conversation`. + """ + + reasoning: Optional[Reasoning] + """ + **gpt-5 and o-series models only** Configuration options for + [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + text: Optional[Text] + """Configuration options for a text response from the model. + + Can be plain text or structured JSON data. Learn more: + + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + tool_choice: Optional[ToolChoice] + """Controls which tool the model should use, if any.""" + + tools: Optional[Iterable[BetaToolParam]] + """An array of tools the model may call while generating a response. + + You can specify which tool to use by setting the `tool_choice` parameter. + """ + + truncation: Literal["auto", "disabled"] + """The truncation strategy to use for the model response. + + - `auto`: If the input to this Response exceeds the model's context window size, + the model will truncate the response to fit the context window by dropping + items from the beginning of the conversation. - `disabled` (default): If the + input size will exceed the context window size for a model, the request will + fail with a 400 error. + """ + + betas: Annotated[List[Literal["responses_multi_agent=v1"]], PropertyInfo(alias="openai-beta")] + + +Conversation: TypeAlias = Union[str, BetaResponseConversationParamParam] + + +class Reasoning(TypedDict, total=False): + """ + **gpt-5 and o-series models only** Configuration options for [reasoning models](https://platform.openai.com/docs/guides/reasoning). + """ + + context: Optional[Literal["auto", "current_turn", "all_turns"]] + """ + Controls which reasoning items are rendered back to the model on later turns. + When returned on a response, this is the effective reasoning context mode used + for the response. + """ + + effort: Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. + """ + + generate_summary: Optional[Literal["auto", "concise", "detailed"]] + """**Deprecated:** use `summary` instead. + + A summary of the reasoning performed by the model. This can be useful for + debugging and understanding the model's reasoning process. One of `auto`, + `concise`, or `detailed`. + """ + + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + + summary: Optional[Literal["auto", "concise", "detailed"]] + """A summary of the reasoning performed by the model. + + This can be useful for debugging and understanding the model's reasoning + process. One of `auto`, `concise`, or `detailed`. + + `concise` is supported for `computer-use-preview` models and all reasoning + models after `gpt-5`. + """ + + +class Text(TypedDict, total=False): + """Configuration options for a text response from the model. + + Can be plain + text or structured JSON data. Learn more: + - [Text inputs and outputs](https://platform.openai.com/docs/guides/text) + - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) + """ + + format: BetaResponseFormatTextConfigParam + """An object specifying the format that the model must output. + + Configuring `{ "type": "json_schema" }` enables Structured Outputs, which + ensures the model will match your supplied JSON schema. Learn more in the + [Structured Outputs guide](https://platform.openai.com/docs/guides/structured-outputs). + + The default format is `{ "type": "text" }` with no additional options. + + **Not recommended for gpt-4o and newer models:** + + Setting to `{ "type": "json_object" }` enables the older JSON mode, which + ensures the message the model generates is valid JSON. Using `json_schema` is + preferred for models that support it. + """ + + verbosity: Optional[Literal["low", "medium", "high"]] + """Constrains the verbosity of the model's response. + + Lower values will result in more concise responses, while higher values will + result in more verbose responses. Currently supported values are `low`, + `medium`, and `high`. + """ + + +class ToolChoiceBetaSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + +ToolChoice: TypeAlias = Union[ + BetaToolChoiceOptions, + BetaToolChoiceAllowedParam, + BetaToolChoiceTypesParam, + BetaToolChoiceFunctionParam, + BetaToolChoiceMcpParam, + BetaToolChoiceCustomParam, + ToolChoiceBetaSpecificProgrammaticToolCallingParam, + BetaToolChoiceApplyPatchParam, + BetaToolChoiceShellParam, +] diff --git a/src/openai/types/beta/responses/input_token_count_response.py b/src/openai/types/beta/responses/input_token_count_response.py new file mode 100644 index 0000000000..37b2e7e49f --- /dev/null +++ b/src/openai/types/beta/responses/input_token_count_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["InputTokenCountResponse"] + + +class InputTokenCountResponse(BaseModel): + input_tokens: int + + object: Literal["response.input_tokens"] diff --git a/src/openai/types/beta/threads/run_create_params.py b/src/openai/types/beta/threads/run_create_params.py index 376afc9aad..572d24c506 100644 --- a/src/openai/types/beta/threads/run_create_params.py +++ b/src/openai/types/beta/threads/run_create_params.py @@ -108,20 +108,14 @@ class RunCreateParamsBase(TypedDict, total=False): """ reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ response_format: Optional[AssistantResponseFormatOptionParam] diff --git a/src/openai/types/chat/chat_completion_content_part_image.py b/src/openai/types/chat/chat_completion_content_part_image.py index a636c51fb4..43a80f0587 100644 --- a/src/openai/types/chat/chat_completion_content_part_image.py +++ b/src/openai/types/chat/chat_completion_content_part_image.py @@ -5,7 +5,7 @@ from ..._models import BaseModel -__all__ = ["ChatCompletionContentPartImage", "ImageURL"] +__all__ = ["ChatCompletionContentPartImage", "ImageURL", "PromptCacheBreakpoint"] class ImageURL(BaseModel): @@ -20,6 +20,16 @@ class ImageURL(BaseModel): """ +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" + + class ChatCompletionContentPartImage(BaseModel): """Learn about [image inputs](https://platform.openai.com/docs/guides/vision).""" @@ -27,3 +37,10 @@ class ChatCompletionContentPartImage(BaseModel): type: Literal["image_url"] """The type of the content part.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/chat/chat_completion_content_part_image_param.py b/src/openai/types/chat/chat_completion_content_part_image_param.py index a230a340a7..fc1fbc2d3e 100644 --- a/src/openai/types/chat/chat_completion_content_part_image_param.py +++ b/src/openai/types/chat/chat_completion_content_part_image_param.py @@ -4,7 +4,7 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["ChatCompletionContentPartImageParam", "ImageURL"] +__all__ = ["ChatCompletionContentPartImageParam", "ImageURL", "PromptCacheBreakpoint"] class ImageURL(TypedDict, total=False): @@ -19,6 +19,16 @@ class ImageURL(TypedDict, total=False): """ +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + class ChatCompletionContentPartImageParam(TypedDict, total=False): """Learn about [image inputs](https://platform.openai.com/docs/guides/vision).""" @@ -26,3 +36,10 @@ class ChatCompletionContentPartImageParam(TypedDict, total=False): type: Required[Literal["image_url"]] """The type of the content part.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py index 98d9e3c5eb..1993c62569 100644 --- a/src/openai/types/chat/chat_completion_content_part_input_audio_param.py +++ b/src/openai/types/chat/chat_completion_content_part_input_audio_param.py @@ -4,7 +4,7 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["ChatCompletionContentPartInputAudioParam", "InputAudio"] +__all__ = ["ChatCompletionContentPartInputAudioParam", "InputAudio", "PromptCacheBreakpoint"] class InputAudio(TypedDict, total=False): @@ -15,6 +15,16 @@ class InputAudio(TypedDict, total=False): """The format of the encoded audio data. Currently supports "wav" and "mp3".""" +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + class ChatCompletionContentPartInputAudioParam(TypedDict, total=False): """Learn about [audio inputs](https://platform.openai.com/docs/guides/audio).""" @@ -22,3 +32,10 @@ class ChatCompletionContentPartInputAudioParam(TypedDict, total=False): type: Required[Literal["input_audio"]] """The type of the content part. Always `input_audio`.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/chat/chat_completion_content_part_param.py b/src/openai/types/chat/chat_completion_content_part_param.py index b8c710a980..667b4f0ae8 100644 --- a/src/openai/types/chat/chat_completion_content_part_param.py +++ b/src/openai/types/chat/chat_completion_content_part_param.py @@ -9,7 +9,7 @@ from .chat_completion_content_part_image_param import ChatCompletionContentPartImageParam from .chat_completion_content_part_input_audio_param import ChatCompletionContentPartInputAudioParam -__all__ = ["ChatCompletionContentPartParam", "File", "FileFile"] +__all__ = ["ChatCompletionContentPartParam", "File", "FileFile", "FilePromptCacheBreakpoint"] class FileFile(TypedDict, total=False): @@ -26,6 +26,16 @@ class FileFile(TypedDict, total=False): """The name of the file, used when passing the file to the model as a string.""" +class FilePromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" + + class File(TypedDict, total=False): """ Learn about [file inputs](https://platform.openai.com/docs/guides/text) for text generation. @@ -36,6 +46,13 @@ class File(TypedDict, total=False): type: Required[Literal["file"]] """The type of the content part. Always `file`.""" + prompt_cache_breakpoint: FilePromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ + ChatCompletionContentPartParam: TypeAlias = Union[ ChatCompletionContentPartTextParam, diff --git a/src/openai/types/chat/chat_completion_content_part_text.py b/src/openai/types/chat/chat_completion_content_part_text.py index e6d1bf1ec0..d76cf886cc 100644 --- a/src/openai/types/chat/chat_completion_content_part_text.py +++ b/src/openai/types/chat/chat_completion_content_part_text.py @@ -1,10 +1,21 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["ChatCompletionContentPartText"] +__all__ = ["ChatCompletionContentPartText", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ChatCompletionContentPartText(BaseModel): @@ -17,3 +28,10 @@ class ChatCompletionContentPartText(BaseModel): type: Literal["text"] """The type of the content part.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/chat/chat_completion_content_part_text_param.py b/src/openai/types/chat/chat_completion_content_part_text_param.py index be69bf66fa..2297683505 100644 --- a/src/openai/types/chat/chat_completion_content_part_text_param.py +++ b/src/openai/types/chat/chat_completion_content_part_text_param.py @@ -4,7 +4,17 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["ChatCompletionContentPartTextParam"] +__all__ = ["ChatCompletionContentPartTextParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" class ChatCompletionContentPartTextParam(TypedDict, total=False): @@ -17,3 +27,10 @@ class ChatCompletionContentPartTextParam(TypedDict, total=False): type: Required[Literal["text"]] """The type of the content part.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index b7de79be72..dfccdb0574 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -26,6 +26,10 @@ "FunctionCall", "Function", "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", "ResponseFormat", "WebSearchOptions", "WebSearchOptionsUserLocation", @@ -189,13 +193,31 @@ class CompletionCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_options: PromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] - """The retention policy for the prompt cache. + """Deprecated. Use `prompt_cache_options.ttl` instead. - Set to `24h` to enable extended prompt caching, which keeps cached prefixes - active for longer, up to a maximum of 24 hours. + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -206,20 +228,14 @@ class CompletionCreateParamsBase(TypedDict, total=False): """ reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. + """Constrains effort on reasoning for reasoning models. - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ response_format: ResponseFormat @@ -392,6 +408,28 @@ class Function(TypedDict, total=False): """ +class ModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] + """The moderation policy for the response output.""" + + class Moderation(TypedDict, total=False): """Configuration for running moderation on the request input and generated output.""" @@ -401,6 +439,33 @@ class Moderation(TypedDict, total=False): 'omni-moderation-latest'. """ + policy: Optional[ModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + ResponseFormat: TypeAlias = Union[ResponseFormatText, ResponseFormatJSONSchema, ResponseFormatJSONObject] diff --git a/src/openai/types/completion_usage.py b/src/openai/types/completion_usage.py index 9b5202da14..1e016193a1 100644 --- a/src/openai/types/completion_usage.py +++ b/src/openai/types/completion_usage.py @@ -37,6 +37,9 @@ class PromptTokensDetails(BaseModel): audio_tokens: Optional[int] = None """Audio input tokens present in the prompt.""" + cache_write_tokens: Optional[int] = None + """The unadjusted number of prompt tokens written to cache.""" + cached_tokens: Optional[int] = None """Cached tokens present in the prompt.""" diff --git a/src/openai/types/conversations/computer_screenshot_content.py b/src/openai/types/conversations/computer_screenshot_content.py index ff43a7e589..4803fa8935 100644 --- a/src/openai/types/conversations/computer_screenshot_content.py +++ b/src/openai/types/conversations/computer_screenshot_content.py @@ -5,7 +5,17 @@ from ..._models import BaseModel -__all__ = ["ComputerScreenshotContent"] +__all__ = ["ComputerScreenshotContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ComputerScreenshotContent(BaseModel): @@ -28,3 +38,10 @@ class ComputerScreenshotContent(BaseModel): For a computer screenshot, this property is always set to `computer_screenshot`. """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/conversations/conversation_item.py b/src/openai/types/conversations/conversation_item.py index 9bc0495a34..b629b362ed 100644 --- a/src/openai/types/conversations/conversation_item.py +++ b/src/openai/types/conversations/conversation_item.py @@ -29,6 +29,8 @@ "ConversationItem", "ImageGenerationCall", "AdditionalTools", + "Program", + "ProgramOutput", "LocalShellCall", "LocalShellCallAction", "LocalShellCallOutput", @@ -70,6 +72,40 @@ class AdditionalTools(BaseModel): """The type of the item. Always `additional_tools`.""" +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + class LocalShellCallAction(BaseModel): """Execute a shell command on the server.""" @@ -252,6 +288,8 @@ class McpCall(BaseModel): ResponseToolSearchOutputItem, AdditionalTools, ResponseReasoningItem, + Program, + ProgramOutput, ResponseCompactionItem, ResponseCodeInterpreterToolCall, LocalShellCall, diff --git a/src/openai/types/evals/create_eval_completions_run_data_source.py b/src/openai/types/evals/create_eval_completions_run_data_source.py index 726ae6abf0..0e48d7afb9 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source.py @@ -188,20 +188,14 @@ class SamplingParams(BaseModel): """The maximum number of tokens in the generated output.""" reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ response_format: Optional[SamplingParamsResponseFormat] = None diff --git a/src/openai/types/evals/create_eval_completions_run_data_source_param.py b/src/openai/types/evals/create_eval_completions_run_data_source_param.py index 6842f84af9..4a2a8afa54 100644 --- a/src/openai/types/evals/create_eval_completions_run_data_source_param.py +++ b/src/openai/types/evals/create_eval_completions_run_data_source_param.py @@ -184,20 +184,14 @@ class SamplingParams(TypedDict, total=False): """The maximum number of tokens in the generated output.""" reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ response_format: SamplingParamsResponseFormat diff --git a/src/openai/types/evals/run_cancel_response.py b/src/openai/types/evals/run_cancel_response.py index ea4797eecb..100f72dc25 100644 --- a/src/openai/types/evals/run_cancel_response.py +++ b/src/openai/types/evals/run_cancel_response.py @@ -103,20 +103,14 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ temperature: Optional[float] = None @@ -270,20 +264,14 @@ class DataSourceResponsesSamplingParams(BaseModel): """The maximum number of tokens in the generated output.""" reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_create_params.py b/src/openai/types/evals/run_create_params.py index 02804c30da..192f2065a7 100644 --- a/src/openai/types/evals/run_create_params.py +++ b/src/openai/types/evals/run_create_params.py @@ -116,20 +116,14 @@ class DataSourceCreateEvalResponsesRunDataSourceSourceResponses(TypedDict, total """ reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ temperature: Optional[float] @@ -288,20 +282,14 @@ class DataSourceCreateEvalResponsesRunDataSourceSamplingParams(TypedDict, total= """The maximum number of tokens in the generated output.""" reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ seed: int diff --git a/src/openai/types/evals/run_create_response.py b/src/openai/types/evals/run_create_response.py index 2cb856de6f..17fb38bdd2 100644 --- a/src/openai/types/evals/run_create_response.py +++ b/src/openai/types/evals/run_create_response.py @@ -103,20 +103,14 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ temperature: Optional[float] = None @@ -270,20 +264,14 @@ class DataSourceResponsesSamplingParams(BaseModel): """The maximum number of tokens in the generated output.""" reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_list_response.py b/src/openai/types/evals/run_list_response.py index defd4aa6f9..224ca345b8 100644 --- a/src/openai/types/evals/run_list_response.py +++ b/src/openai/types/evals/run_list_response.py @@ -103,20 +103,14 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ temperature: Optional[float] = None @@ -270,20 +264,14 @@ class DataSourceResponsesSamplingParams(BaseModel): """The maximum number of tokens in the generated output.""" reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ seed: Optional[int] = None diff --git a/src/openai/types/evals/run_retrieve_response.py b/src/openai/types/evals/run_retrieve_response.py index 4c218a0510..fc2c6f442b 100644 --- a/src/openai/types/evals/run_retrieve_response.py +++ b/src/openai/types/evals/run_retrieve_response.py @@ -103,20 +103,14 @@ class DataSourceResponsesSourceResponses(BaseModel): """ reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ temperature: Optional[float] = None @@ -270,20 +264,14 @@ class DataSourceResponsesSamplingParams(BaseModel): """The maximum number of tokens in the generated output.""" reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader.py b/src/openai/types/graders/score_model_grader.py index 85d11e8666..73babb0162 100644 --- a/src/openai/types/graders/score_model_grader.py +++ b/src/openai/types/graders/score_model_grader.py @@ -83,20 +83,14 @@ class SamplingParams(BaseModel): """The maximum number of tokens the grader model may generate in its response.""" reasoning_effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ seed: Optional[int] = None diff --git a/src/openai/types/graders/score_model_grader_param.py b/src/openai/types/graders/score_model_grader_param.py index 9f1c42e051..f727aeadf7 100644 --- a/src/openai/types/graders/score_model_grader_param.py +++ b/src/openai/types/graders/score_model_grader_param.py @@ -89,20 +89,14 @@ class SamplingParams(TypedDict, total=False): """The maximum number of tokens the grader model may generate in its response.""" reasoning_effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ seed: Optional[int] diff --git a/src/openai/types/realtime/call_accept_params.py b/src/openai/types/realtime/call_accept_params.py index b4a48fc8b5..231b30b40d 100644 --- a/src/openai/types/realtime/call_accept_params.py +++ b/src/openai/types/realtime/call_accept_params.py @@ -59,6 +59,8 @@ class CallAcceptParams(TypedDict, total=False): "gpt-realtime", "gpt-realtime-1.5", "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool.py b/src/openai/types/realtime/realtime_response_create_mcp_tool.py index bff59b7ae6..6e3c3512af 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool.py @@ -94,6 +94,9 @@ class RealtimeResponseCreateMcpTool(BaseModel): type: Literal["mcp"] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + allowed_tools: Optional[AllowedTools] = None """List of allowed tool names or a filter object.""" diff --git a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py index 4cc427cc13..51ade5ad0f 100644 --- a/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py +++ b/src/openai/types/realtime/realtime_response_create_mcp_tool_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr @@ -96,6 +96,9 @@ class RealtimeResponseCreateMcpToolParam(TypedDict, total=False): type: Required[Literal["mcp"]] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + allowed_tools: Optional[AllowedTools] """List of allowed tool names or a filter object.""" diff --git a/src/openai/types/realtime/realtime_session_create_request.py b/src/openai/types/realtime/realtime_session_create_request.py index cf681e99a1..a1088415a9 100644 --- a/src/openai/types/realtime/realtime_session_create_request.py +++ b/src/openai/types/realtime/realtime_session_create_request.py @@ -60,6 +60,8 @@ class RealtimeSessionCreateRequest(BaseModel): "gpt-realtime", "gpt-realtime-1.5", "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", diff --git a/src/openai/types/realtime/realtime_session_create_request_param.py b/src/openai/types/realtime/realtime_session_create_request_param.py index ab7de47c3c..cf5787229d 100644 --- a/src/openai/types/realtime/realtime_session_create_request_param.py +++ b/src/openai/types/realtime/realtime_session_create_request_param.py @@ -61,6 +61,8 @@ class RealtimeSessionCreateRequestParam(TypedDict, total=False): "gpt-realtime", "gpt-realtime-1.5", "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", diff --git a/src/openai/types/realtime/realtime_session_create_response.py b/src/openai/types/realtime/realtime_session_create_response.py index 7cd3ce189a..129f36b78c 100644 --- a/src/openai/types/realtime/realtime_session_create_response.py +++ b/src/openai/types/realtime/realtime_session_create_response.py @@ -316,6 +316,9 @@ class ToolMcpTool(BaseModel): type: Literal["mcp"] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + allowed_tools: Optional[ToolMcpToolAllowedTools] = None """List of allowed tool names or a filter object.""" @@ -466,6 +469,8 @@ class RealtimeSessionCreateResponse(BaseModel): "gpt-realtime", "gpt-realtime-1.5", "gpt-realtime-2", + "gpt-realtime-2.1", + "gpt-realtime-2.1-mini", "gpt-realtime-2025-08-28", "gpt-4o-realtime-preview", "gpt-4o-realtime-preview-2024-10-01", diff --git a/src/openai/types/realtime/realtime_tools_config_param.py b/src/openai/types/realtime/realtime_tools_config_param.py index 0a9ec2fdd9..1e5903270e 100644 --- a/src/openai/types/realtime/realtime_tools_config_param.py +++ b/src/openai/types/realtime/realtime_tools_config_param.py @@ -99,6 +99,9 @@ class Mcp(TypedDict, total=False): type: Required[Literal["mcp"]] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + allowed_tools: Optional[McpAllowedTools] """List of allowed tool names or a filter object.""" diff --git a/src/openai/types/realtime/realtime_tools_config_union.py b/src/openai/types/realtime/realtime_tools_config_union.py index 968a096475..d7023dff0c 100644 --- a/src/openai/types/realtime/realtime_tools_config_union.py +++ b/src/openai/types/realtime/realtime_tools_config_union.py @@ -97,6 +97,9 @@ class Mcp(BaseModel): type: Literal["mcp"] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + allowed_tools: Optional[McpAllowedTools] = None """List of allowed tool names or a filter object.""" diff --git a/src/openai/types/realtime/realtime_tools_config_union_param.py b/src/openai/types/realtime/realtime_tools_config_union_param.py index f84b6f7ce4..2fac844a1c 100644 --- a/src/openai/types/realtime/realtime_tools_config_union_param.py +++ b/src/openai/types/realtime/realtime_tools_config_union_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr @@ -98,6 +98,9 @@ class Mcp(TypedDict, total=False): type: Required[Literal["mcp"]] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + allowed_tools: Optional[McpAllowedTools] """List of allowed tool names or a filter object.""" diff --git a/src/openai/types/responses/apply_patch_tool.py b/src/openai/types/responses/apply_patch_tool.py index f2ed245d10..45f644648f 100644 --- a/src/openai/types/responses/apply_patch_tool.py +++ b/src/openai/types/responses/apply_patch_tool.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel @@ -12,3 +13,6 @@ class ApplyPatchTool(BaseModel): type: Literal["apply_patch"] """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" diff --git a/src/openai/types/responses/apply_patch_tool_param.py b/src/openai/types/responses/apply_patch_tool_param.py index 2e0a809099..95a722cd11 100644 --- a/src/openai/types/responses/apply_patch_tool_param.py +++ b/src/openai/types/responses/apply_patch_tool_param.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import List, Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ApplyPatchToolParam"] @@ -12,3 +13,6 @@ class ApplyPatchToolParam(TypedDict, total=False): type: Required[Literal["apply_patch"]] """The type of the tool. Always `apply_patch`.""" + + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" diff --git a/src/openai/types/responses/custom_tool.py b/src/openai/types/responses/custom_tool.py index 017c6d699b..99a860d4bf 100644 --- a/src/openai/types/responses/custom_tool.py +++ b/src/openai/types/responses/custom_tool.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel @@ -21,6 +21,9 @@ class CustomTool(BaseModel): type: Literal["custom"] """The type of the custom tool. Always `custom`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + defer_loading: Optional[bool] = None """Whether this tool should be deferred and discovered via tool search.""" diff --git a/src/openai/types/responses/custom_tool_param.py b/src/openai/types/responses/custom_tool_param.py index e64001366e..d3626ea8d1 100644 --- a/src/openai/types/responses/custom_tool_param.py +++ b/src/openai/types/responses/custom_tool_param.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..shared_params.custom_tool_input_format import CustomToolInputFormat @@ -21,6 +22,9 @@ class CustomToolParam(TypedDict, total=False): type: Required[Literal["custom"]] """The type of the custom tool. Always `custom`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + defer_loading: bool """Whether this tool should be deferred and discovered via tool search.""" diff --git a/src/openai/types/responses/function_shell_tool.py b/src/openai/types/responses/function_shell_tool.py index 17d6bb36c9..3ef4eb22e1 100644 --- a/src/openai/types/responses/function_shell_tool.py +++ b/src/openai/types/responses/function_shell_tool.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -22,4 +22,7 @@ class FunctionShellTool(BaseModel): type: Literal["shell"] """The type of the shell tool. Always `shell`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + environment: Optional[Environment] = None diff --git a/src/openai/types/responses/function_shell_tool_param.py b/src/openai/types/responses/function_shell_tool_param.py index b8464ed341..135a530ba1 100644 --- a/src/openai/types/responses/function_shell_tool_param.py +++ b/src/openai/types/responses/function_shell_tool_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Optional +from typing import List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .container_auto_param import ContainerAutoParam @@ -20,4 +20,7 @@ class FunctionShellToolParam(TypedDict, total=False): type: Required[Literal["shell"]] """The type of the shell tool. Always `shell`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + environment: Optional[Environment] diff --git a/src/openai/types/responses/function_tool.py b/src/openai/types/responses/function_tool.py index 6e9751ad88..b46f63ed0a 100644 --- a/src/openai/types/responses/function_tool.py +++ b/src/openai/types/responses/function_tool.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, Optional +from typing import Dict, List, Optional from typing_extensions import Literal from ..._models import BaseModel @@ -21,11 +21,14 @@ class FunctionTool(BaseModel): """A JSON schema object describing the parameters of the function.""" strict: Optional[bool] = None - """Whether to enforce strict parameter validation. Default `true`.""" + """Whether strict parameter validation is enforced for this function tool.""" type: Literal["function"] """The type of the function tool. Always `function`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + defer_loading: Optional[bool] = None """Whether this function is deferred and loaded via tool search.""" @@ -34,3 +37,9 @@ class FunctionTool(BaseModel): Used by the model to determine whether or not to call the function. """ + + output_schema: Optional[Dict[str, object]] = None + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/src/openai/types/responses/function_tool_param.py b/src/openai/types/responses/function_tool_param.py index e7978b44a2..5155f0405e 100644 --- a/src/openai/types/responses/function_tool_param.py +++ b/src/openai/types/responses/function_tool_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Optional +from typing import Dict, List, Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["FunctionToolParam"] @@ -21,11 +21,14 @@ class FunctionToolParam(TypedDict, total=False): """A JSON schema object describing the parameters of the function.""" strict: Required[Optional[bool]] - """Whether to enforce strict parameter validation. Default `true`.""" + """Whether strict parameter validation is enforced for this function tool.""" type: Required[Literal["function"]] """The type of the function tool. Always `function`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + defer_loading: bool """Whether this function is deferred and loaded via tool search.""" @@ -34,3 +37,9 @@ class FunctionToolParam(TypedDict, total=False): Used by the model to determine whether or not to call the function. """ + + output_schema: Optional[Dict[str, object]] + """ + A JSON schema object describing the JSON value encoded in string outputs for + this function. + """ diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py index d94eea689c..78ed01f432 100644 --- a/src/openai/types/responses/input_token_count_params.py +++ b/src/openai/types/responses/input_token_count_params.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Union, Iterable, Optional -from typing_extensions import Literal, TypeAlias, TypedDict +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .tool_param import ToolParam from .tool_choice_options import ToolChoiceOptions @@ -19,7 +19,13 @@ from .response_conversation_param_param import ResponseConversationParamParam from .response_format_text_config_param import ResponseFormatTextConfigParam -__all__ = ["InputTokenCountParams", "Conversation", "Text", "ToolChoice"] +__all__ = [ + "InputTokenCountParams", + "Conversation", + "Text", + "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", +] class InputTokenCountParams(TypedDict, total=False): @@ -141,6 +147,11 @@ class Text(TypedDict, total=False): """ +class ToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + ToolChoice: TypeAlias = Union[ ToolChoiceOptions, ToolChoiceAllowedParam, @@ -148,6 +159,7 @@ class Text(TypedDict, total=False): ToolChoiceFunctionParam, ToolChoiceMcpParam, ToolChoiceCustomParam, + ToolChoiceSpecificProgrammaticToolCallingParam, ToolChoiceApplyPatchParam, ToolChoiceShellParam, ] diff --git a/src/openai/types/responses/namespace_tool.py b/src/openai/types/responses/namespace_tool.py index 88f76a9732..012d2e9e70 100644 --- a/src/openai/types/responses/namespace_tool.py +++ b/src/openai/types/responses/namespace_tool.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo @@ -15,14 +15,28 @@ class ToolFunction(BaseModel): type: Literal["function"] + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + defer_loading: Optional[bool] = None """Whether this function should be deferred and discovered via tool search.""" description: Optional[str] = None + output_schema: Optional[Dict[str, object]] = None + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + parameters: Optional[object] = None strict: Optional[bool] = None + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ Tool: TypeAlias = Annotated[Union[ToolFunction, CustomTool], PropertyInfo(discriminator="type")] diff --git a/src/openai/types/responses/namespace_tool_param.py b/src/openai/types/responses/namespace_tool_param.py index cb1e5e17f4..df721cf911 100644 --- a/src/openai/types/responses/namespace_tool_param.py +++ b/src/openai/types/responses/namespace_tool_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Iterable, Optional +from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .custom_tool_param import CustomToolParam @@ -15,14 +15,28 @@ class ToolFunction(TypedDict, total=False): type: Required[Literal["function"]] + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + defer_loading: bool """Whether this function should be deferred and discovered via tool search.""" description: Optional[str] + output_schema: Optional[Dict[str, object]] + """ + A JSON Schema describing the JSON value encoded in string outputs for this + function tool. This does not describe content-array outputs. + """ + parameters: Optional[object] strict: Optional[bool] + """Whether to enforce strict parameter validation. + + If omitted, Responses attempts to use strict validation when the schema is + compatible, and falls back to non-strict validation otherwise. + """ Tool: TypeAlias = Union[ToolFunction, CustomToolParam] diff --git a/src/openai/types/responses/parsed_response.py b/src/openai/types/responses/parsed_response.py index 0d1f18b472..7e62ff6139 100644 --- a/src/openai/types/responses/parsed_response.py +++ b/src/openai/types/responses/parsed_response.py @@ -8,7 +8,9 @@ from ..._models import GenericModel from .response_output_item import ( McpCall, + Program, McpListTools, + ProgramOutput, LocalShellCall, AdditionalTools, McpApprovalRequest, @@ -83,6 +85,8 @@ class ParsedResponseFunctionToolCall(ResponseFunctionToolCall): ResponseToolSearchOutputItem, AdditionalTools, ResponseReasoningItem, + Program, + ProgramOutput, McpCall, McpApprovalRequest, McpApprovalResponse, diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 67102d2628..e94ad18b34 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -29,6 +29,7 @@ "Response", "IncompleteDetails", "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", "Conversation", "Moderation", "ModerationInput", @@ -37,6 +38,7 @@ "ModerationOutput", "ModerationOutputModerationResult", "ModerationOutputError", + "PromptCacheOptions", ] @@ -47,6 +49,11 @@ class IncompleteDetails(BaseModel): """The reason why the response is incomplete.""" +class ToolChoiceSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + ToolChoice: TypeAlias = Union[ ToolChoiceOptions, ToolChoiceAllowed, @@ -54,6 +61,7 @@ class IncompleteDetails(BaseModel): ToolChoiceFunction, ToolChoiceMcp, ToolChoiceCustom, + ToolChoiceSpecificProgrammaticToolCallingParam, ToolChoiceApplyPatch, ToolChoiceShell, ] @@ -173,6 +181,19 @@ class Moderation(BaseModel): """Moderation for the response output.""" +class PromptCacheOptions(BaseModel): + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + + mode: Literal["implicit", "explicit"] + """Whether implicit prompt-cache breakpoints were enabled.""" + + ttl: Literal["30m"] + """The minimum lifetime applied to each cache breakpoint.""" + + class Response(BaseModel): id: str """Unique identifier for this Response.""" @@ -337,13 +358,23 @@ class Response(BaseModel): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_options: Optional[PromptCacheOptions] = None + """The prompt-caching options that were applied to the response. + + Supported for `gpt-5.6` and later models. + """ + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None - """The retention policy for the prompt cache. + """Deprecated. Use `prompt_cache_options.ttl` instead. - Set to `24h` to enable extended prompt caching, which keeps cached prefixes - active for longer, up to a maximum of 24 hours. + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: diff --git a/src/openai/types/responses/response_apply_patch_tool_call.py b/src/openai/types/responses/response_apply_patch_tool_call.py index 7af1300265..8dfa8bd747 100644 --- a/src/openai/types/responses/response_apply_patch_tool_call.py +++ b/src/openai/types/responses/response_apply_patch_tool_call.py @@ -12,6 +12,9 @@ "OperationCreateFile", "OperationDeleteFile", "OperationUpdateFile", + "Caller", + "CallerDirect", + "CallerProgram", ] @@ -56,6 +59,20 @@ class OperationUpdateFile(BaseModel): ] +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + class ResponseApplyPatchToolCall(BaseModel): """A tool call that applies file diffs by creating, deleting, or updating files.""" @@ -80,5 +97,8 @@ class ResponseApplyPatchToolCall(BaseModel): type: Literal["apply_patch_call"] """The type of the item. Always `apply_patch_call`.""" + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + created_by: Optional[str] = None """The ID of the entity that created this tool call.""" diff --git a/src/openai/types/responses/response_apply_patch_tool_call_output.py b/src/openai/types/responses/response_apply_patch_tool_call_output.py index de63c6e2ee..cc375f58ce 100644 --- a/src/openai/types/responses/response_apply_patch_tool_call_output.py +++ b/src/openai/types/responses/response_apply_patch_tool_call_output.py @@ -1,11 +1,26 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel -__all__ = ["ResponseApplyPatchToolCallOutput"] +__all__ = ["ResponseApplyPatchToolCallOutput", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] class ResponseApplyPatchToolCallOutput(BaseModel): @@ -26,6 +41,9 @@ class ResponseApplyPatchToolCallOutput(BaseModel): type: Literal["apply_patch_call_output"] """The type of the item. Always `apply_patch_call_output`.""" + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + created_by: Optional[str] = None """The ID of the entity that created this tool call output.""" diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index 923a09e56d..3f3bc73465 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -7,13 +7,16 @@ from .response_input_item_param import ResponseInputItemParam -__all__ = ["ResponseCompactParams"] +__all__ = ["ResponseCompactParams", "PromptCacheOptions"] class ResponseCompactParams(TypedDict, total=False): model: Required[ Union[ Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano", @@ -141,8 +144,46 @@ class ResponseCompactParams(TypedDict, total=False): prompt_cache_key: Optional[str] """A key to use when reading from or writing to the prompt cache.""" + prompt_cache_options: Optional[PromptCacheOptions] + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] """How long to retain a prompt cache entry created by this request.""" service_tier: Optional[Literal["auto", "default", "flex", "priority"]] """The service tier to use for this request.""" + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index 1b273928a1..f775970eff 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -28,8 +28,13 @@ "ContextManagement", "Conversation", "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", "StreamOptions", "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", "ResponseCreateParamsNonStreaming", "ResponseCreateParamsStreaming", ] @@ -156,13 +161,31 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_options: PromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] - """The retention policy for the prompt cache. + """Deprecated. Use `prompt_cache_options.ttl` instead. - Set to `24h` to enable extended prompt caching, which keeps cached prefixes - active for longer, up to a maximum of 24 hours. + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: @@ -308,6 +331,28 @@ class ContextManagement(TypedDict, total=False): Conversation: TypeAlias = Union[str, ResponseConversationParamParam] +class ModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] + """The moderation policy for the response output.""" + + class Moderation(TypedDict, total=False): """Configuration for running moderation on the input and output of this response.""" @@ -317,6 +362,33 @@ class Moderation(TypedDict, total=False): 'omni-moderation-latest'. """ + policy: Optional[ModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + class StreamOptions(TypedDict, total=False): """Options for streaming responses. Only set this when you set `stream: true`.""" @@ -333,6 +405,11 @@ class StreamOptions(TypedDict, total=False): """ +class ToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + ToolChoice: TypeAlias = Union[ ToolChoiceOptions, ToolChoiceAllowedParam, @@ -340,6 +417,7 @@ class StreamOptions(TypedDict, total=False): ToolChoiceFunctionParam, ToolChoiceMcpParam, ToolChoiceCustomParam, + ToolChoiceSpecificProgrammaticToolCallingParam, ToolChoiceApplyPatchParam, ToolChoiceShellParam, ] diff --git a/src/openai/types/responses/response_custom_tool_call.py b/src/openai/types/responses/response_custom_tool_call.py index 965ed88f96..5831853ff3 100644 --- a/src/openai/types/responses/response_custom_tool_call.py +++ b/src/openai/types/responses/response_custom_tool_call.py @@ -1,11 +1,26 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel -__all__ = ["ResponseCustomToolCall"] +__all__ = ["ResponseCustomToolCall", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] class ResponseCustomToolCall(BaseModel): @@ -26,5 +41,8 @@ class ResponseCustomToolCall(BaseModel): id: Optional[str] = None """The unique ID of the custom tool call in the OpenAI platform.""" + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + namespace: Optional[str] = None """The namespace of the custom tool being called.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output.py b/src/openai/types/responses/response_custom_tool_call_output.py index 833956493b..6e58e3b253 100644 --- a/src/openai/types/responses/response_custom_tool_call_output.py +++ b/src/openai/types/responses/response_custom_tool_call_output.py @@ -9,13 +9,29 @@ from .response_input_text import ResponseInputText from .response_input_image import ResponseInputImage -__all__ = ["ResponseCustomToolCallOutput", "OutputOutputContentList"] +__all__ = ["ResponseCustomToolCallOutput", "OutputOutputContentList", "Caller", "CallerDirect", "CallerProgram"] OutputOutputContentList: TypeAlias = Annotated[ Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") ] +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + class ResponseCustomToolCallOutput(BaseModel): """The output of a custom tool call from your code, being sent back to the model.""" @@ -33,3 +49,6 @@ class ResponseCustomToolCallOutput(BaseModel): id: Optional[str] = None """The unique ID of the custom tool call output in the OpenAI platform.""" + + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" diff --git a/src/openai/types/responses/response_custom_tool_call_output_param.py b/src/openai/types/responses/response_custom_tool_call_output_param.py index db0034216a..2a6af1b8c0 100644 --- a/src/openai/types/responses/response_custom_tool_call_output_param.py +++ b/src/openai/types/responses/response_custom_tool_call_output_param.py @@ -2,18 +2,34 @@ from __future__ import annotations -from typing import Union, Iterable +from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .response_input_file_param import ResponseInputFileParam from .response_input_text_param import ResponseInputTextParam from .response_input_image_param import ResponseInputImageParam -__all__ = ["ResponseCustomToolCallOutputParam", "OutputOutputContentList"] +__all__ = ["ResponseCustomToolCallOutputParam", "OutputOutputContentList", "Caller", "CallerDirect", "CallerProgram"] OutputOutputContentList: TypeAlias = Union[ResponseInputTextParam, ResponseInputImageParam, ResponseInputFileParam] +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] + + class ResponseCustomToolCallOutputParam(TypedDict, total=False): """The output of a custom tool call from your code, being sent back to the model.""" @@ -31,3 +47,6 @@ class ResponseCustomToolCallOutputParam(TypedDict, total=False): id: str """The unique ID of the custom tool call output in the OpenAI platform.""" + + caller: Optional[Caller] + """The execution context that produced this tool call.""" diff --git a/src/openai/types/responses/response_custom_tool_call_param.py b/src/openai/types/responses/response_custom_tool_call_param.py index 9f82546ef1..dc645ea098 100644 --- a/src/openai/types/responses/response_custom_tool_call_param.py +++ b/src/openai/types/responses/response_custom_tool_call_param.py @@ -2,9 +2,24 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["ResponseCustomToolCallParam"] +__all__ = ["ResponseCustomToolCallParam", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] class ResponseCustomToolCallParam(TypedDict, total=False): @@ -25,5 +40,8 @@ class ResponseCustomToolCallParam(TypedDict, total=False): id: str """The unique ID of the custom tool call in the OpenAI platform.""" + caller: Optional[Caller] + """The execution context that produced this tool call.""" + namespace: str """The namespace of the custom tool being called.""" diff --git a/src/openai/types/responses/response_error.py b/src/openai/types/responses/response_error.py index 90958d1c13..b38a9bada8 100644 --- a/src/openai/types/responses/response_error.py +++ b/src/openai/types/responses/response_error.py @@ -14,6 +14,7 @@ class ResponseError(BaseModel): "server_error", "rate_limit_exceeded", "invalid_prompt", + "bio_policy", "vector_store_timeout", "invalid_image", "invalid_image_format", diff --git a/src/openai/types/responses/response_function_shell_tool_call.py b/src/openai/types/responses/response_function_shell_tool_call.py index 22c75cf043..c9572c9644 100644 --- a/src/openai/types/responses/response_function_shell_tool_call.py +++ b/src/openai/types/responses/response_function_shell_tool_call.py @@ -8,7 +8,7 @@ from .response_local_environment import ResponseLocalEnvironment from .response_container_reference import ResponseContainerReference -__all__ = ["ResponseFunctionShellToolCall", "Action", "Environment"] +__all__ = ["ResponseFunctionShellToolCall", "Action", "Environment", "Caller", "CallerDirect", "CallerProgram"] class Action(BaseModel): @@ -28,6 +28,20 @@ class Action(BaseModel): ] +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + class ResponseFunctionShellToolCall(BaseModel): """A tool call that executes one or more shell commands in a managed environment.""" @@ -55,5 +69,8 @@ class ResponseFunctionShellToolCall(BaseModel): type: Literal["shell_call"] """The type of the item. Always `shell_call`.""" + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + created_by: Optional[str] = None """The ID of the entity that created this tool call.""" diff --git a/src/openai/types/responses/response_function_shell_tool_call_output.py b/src/openai/types/responses/response_function_shell_tool_call_output.py index 7196ab47f7..b3346b8650 100644 --- a/src/openai/types/responses/response_function_shell_tool_call_output.py +++ b/src/openai/types/responses/response_function_shell_tool_call_output.py @@ -12,6 +12,9 @@ "OutputOutcome", "OutputOutcomeTimeout", "OutputOutcomeExit", + "Caller", + "CallerDirect", + "CallerProgram", ] @@ -54,6 +57,20 @@ class Output(BaseModel): """The identifier of the actor that created the item.""" +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + class ResponseFunctionShellToolCallOutput(BaseModel): """The output of a shell tool call that was emitted.""" @@ -84,5 +101,8 @@ class ResponseFunctionShellToolCallOutput(BaseModel): type: Literal["shell_call_output"] """The type of the shell call output. Always `shell_call_output`.""" + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + created_by: Optional[str] = None """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_function_tool_call.py b/src/openai/types/responses/response_function_tool_call.py index 3ff4c67a3f..5a352a208f 100644 --- a/src/openai/types/responses/response_function_tool_call.py +++ b/src/openai/types/responses/response_function_tool_call.py @@ -1,11 +1,26 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional -from typing_extensions import Literal +from typing import Union, Optional +from typing_extensions import Literal, Annotated, TypeAlias +from ..._utils import PropertyInfo from ..._models import BaseModel -__all__ = ["ResponseFunctionToolCall"] +__all__ = ["ResponseFunctionToolCall", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(BaseModel): + type: Literal["direct"] + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] class ResponseFunctionToolCall(BaseModel): @@ -30,6 +45,9 @@ class ResponseFunctionToolCall(BaseModel): id: Optional[str] = None """The unique ID of the function tool call.""" + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + namespace: Optional[str] = None """The namespace of the function to run.""" diff --git a/src/openai/types/responses/response_function_tool_call_output_item.py b/src/openai/types/responses/response_function_tool_call_output_item.py index e40feeb37e..a101152edf 100644 --- a/src/openai/types/responses/response_function_tool_call_output_item.py +++ b/src/openai/types/responses/response_function_tool_call_output_item.py @@ -9,13 +9,29 @@ from .response_input_text import ResponseInputText from .response_input_image import ResponseInputImage -__all__ = ["ResponseFunctionToolCallOutputItem", "OutputOutputContentList"] +__all__ = ["ResponseFunctionToolCallOutputItem", "OutputOutputContentList", "Caller", "CallerDirect", "CallerProgram"] OutputOutputContentList: TypeAlias = Annotated[ Union[ResponseInputText, ResponseInputImage, ResponseInputFile], PropertyInfo(discriminator="type") ] +class CallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class CallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +Caller: TypeAlias = Annotated[Union[CallerDirect, CallerProgram, None], PropertyInfo(discriminator="type")] + + class ResponseFunctionToolCallOutputItem(BaseModel): id: str """The unique ID of the function call tool output.""" @@ -39,5 +55,8 @@ class ResponseFunctionToolCallOutputItem(BaseModel): type: Literal["function_call_output"] """The type of the function tool call output. Always `function_call_output`.""" + caller: Optional[Caller] = None + """The execution context that produced this tool call.""" + created_by: Optional[str] = None """The identifier of the actor that created the item.""" diff --git a/src/openai/types/responses/response_function_tool_call_param.py b/src/openai/types/responses/response_function_tool_call_param.py index 5183e9e233..c043caf4cd 100644 --- a/src/openai/types/responses/response_function_tool_call_param.py +++ b/src/openai/types/responses/response_function_tool_call_param.py @@ -2,9 +2,24 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union, Optional +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["ResponseFunctionToolCallParam"] +__all__ = ["ResponseFunctionToolCallParam", "Caller", "CallerDirect", "CallerProgram"] + + +class CallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + + +class CallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + + +Caller: TypeAlias = Union[CallerDirect, CallerProgram] class ResponseFunctionToolCallParam(TypedDict, total=False): @@ -29,6 +44,9 @@ class ResponseFunctionToolCallParam(TypedDict, total=False): id: str """The unique ID of the function tool call.""" + caller: Optional[Caller] + """The execution context that produced this tool call.""" + namespace: str """The namespace of the function to run.""" diff --git a/src/openai/types/responses/response_input_file.py b/src/openai/types/responses/response_input_file.py index f07ff7c049..9d6557dd2d 100644 --- a/src/openai/types/responses/response_input_file.py +++ b/src/openai/types/responses/response_input_file.py @@ -5,7 +5,17 @@ from ..._models import BaseModel -__all__ = ["ResponseInputFile"] +__all__ = ["ResponseInputFile", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ResponseInputFile(BaseModel): @@ -14,11 +24,13 @@ class ResponseInputFile(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" - detail: Optional[Literal["low", "high"]] = None + detail: Optional[Literal["auto", "low", "high"]] = None """The detail level of the file to be sent to the model. - Use `low` for the default rendering behavior, or `high` to render the file at - higher quality. Defaults to `low`. + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. """ file_data: Optional[str] = None @@ -32,3 +44,10 @@ class ResponseInputFile(BaseModel): filename: Optional[str] = None """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_file_content.py b/src/openai/types/responses/response_input_file_content.py index a0c2de4823..0fb34b3791 100644 --- a/src/openai/types/responses/response_input_file_content.py +++ b/src/openai/types/responses/response_input_file_content.py @@ -5,7 +5,17 @@ from ..._models import BaseModel -__all__ = ["ResponseInputFileContent"] +__all__ = ["ResponseInputFileContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ResponseInputFileContent(BaseModel): @@ -14,11 +24,13 @@ class ResponseInputFileContent(BaseModel): type: Literal["input_file"] """The type of the input item. Always `input_file`.""" - detail: Optional[Literal["low", "high"]] = None + detail: Optional[Literal["auto", "low", "high"]] = None """The detail level of the file to be sent to the model. - Use `low` for the default rendering behavior, or `high` to render the file at - higher quality. Defaults to `low`. + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. """ file_data: Optional[str] = None @@ -32,3 +44,10 @@ class ResponseInputFileContent(BaseModel): filename: Optional[str] = None """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_file_content_param.py b/src/openai/types/responses/response_input_file_content_param.py index 4206ea09f3..08583bd638 100644 --- a/src/openai/types/responses/response_input_file_content_param.py +++ b/src/openai/types/responses/response_input_file_content_param.py @@ -5,7 +5,17 @@ from typing import Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["ResponseInputFileContentParam"] +__all__ = ["ResponseInputFileContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" class ResponseInputFileContentParam(TypedDict, total=False): @@ -14,11 +24,13 @@ class ResponseInputFileContentParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" - detail: Literal["low", "high"] + detail: Literal["auto", "low", "high"] """The detail level of the file to be sent to the model. - Use `low` for the default rendering behavior, or `high` to render the file at - higher quality. Defaults to `low`. + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. """ file_data: Optional[str] @@ -32,3 +44,10 @@ class ResponseInputFileContentParam(TypedDict, total=False): filename: Optional[str] """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_file_param.py b/src/openai/types/responses/response_input_file_param.py index a6bdb6e18c..a0af36bcfe 100644 --- a/src/openai/types/responses/response_input_file_param.py +++ b/src/openai/types/responses/response_input_file_param.py @@ -5,7 +5,17 @@ from typing import Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["ResponseInputFileParam"] +__all__ = ["ResponseInputFileParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" class ResponseInputFileParam(TypedDict, total=False): @@ -14,11 +24,13 @@ class ResponseInputFileParam(TypedDict, total=False): type: Required[Literal["input_file"]] """The type of the input item. Always `input_file`.""" - detail: Literal["low", "high"] + detail: Literal["auto", "low", "high"] """The detail level of the file to be sent to the model. - Use `low` for the default rendering behavior, or `high` to render the file at - higher quality. Defaults to `low`. + Use `auto` to let the system select the detail level; for GPT-5.6 and later + models, `auto` uses high-quality rendering, which may increase input token + usage. Use `low` for lower-cost rendering, or `high` to render the file at + higher quality. Defaults to `auto`. """ file_data: str @@ -32,3 +44,10 @@ class ResponseInputFileParam(TypedDict, total=False): filename: str """The name of the file to be sent to the model.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_image.py b/src/openai/types/responses/response_input_image.py index 63c4b42cbd..fa0bd4b1a7 100644 --- a/src/openai/types/responses/response_input_image.py +++ b/src/openai/types/responses/response_input_image.py @@ -5,7 +5,17 @@ from ..._models import BaseModel -__all__ = ["ResponseInputImage"] +__all__ = ["ResponseInputImage", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ResponseInputImage(BaseModel): @@ -31,3 +41,10 @@ class ResponseInputImage(BaseModel): A fully qualified URL or base64 encoded image in a data URL. """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_image_content.py b/src/openai/types/responses/response_input_image_content.py index d9619f65d8..741abb6193 100644 --- a/src/openai/types/responses/response_input_image_content.py +++ b/src/openai/types/responses/response_input_image_content.py @@ -5,7 +5,17 @@ from ..._models import BaseModel -__all__ = ["ResponseInputImageContent"] +__all__ = ["ResponseInputImageContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ResponseInputImageContent(BaseModel): @@ -31,3 +41,10 @@ class ResponseInputImageContent(BaseModel): A fully qualified URL or base64 encoded image in a data URL. """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_image_content_param.py b/src/openai/types/responses/response_input_image_content_param.py index 743642d4cf..f6531ff10e 100644 --- a/src/openai/types/responses/response_input_image_content_param.py +++ b/src/openai/types/responses/response_input_image_content_param.py @@ -5,7 +5,17 @@ from typing import Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["ResponseInputImageContentParam"] +__all__ = ["ResponseInputImageContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" class ResponseInputImageContentParam(TypedDict, total=False): @@ -31,3 +41,10 @@ class ResponseInputImageContentParam(TypedDict, total=False): A fully qualified URL or base64 encoded image in a data URL. """ + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_image_param.py b/src/openai/types/responses/response_input_image_param.py index 118f0b5f72..8c75feb85f 100644 --- a/src/openai/types/responses/response_input_image_param.py +++ b/src/openai/types/responses/response_input_image_param.py @@ -5,7 +5,17 @@ from typing import Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["ResponseInputImageParam"] +__all__ = ["ResponseInputImageParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" class ResponseInputImageParam(TypedDict, total=False): @@ -31,3 +41,10 @@ class ResponseInputImageParam(TypedDict, total=False): A fully qualified URL or base64 encoded image in a data URL. """ + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_item.py b/src/openai/types/responses/response_input_item.py index f7f67c3414..d28c956ea1 100644 --- a/src/openai/types/responses/response_input_item.py +++ b/src/openai/types/responses/response_input_item.py @@ -31,6 +31,9 @@ "ComputerCallOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", "ToolSearchCall", "AdditionalTools", "ImageGenerationCall", @@ -39,14 +42,26 @@ "LocalShellCallOutput", "ShellCall", "ShellCallAction", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", "ShellCallEnvironment", "ShellCallOutput", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", "ApplyPatchCall", "ApplyPatchCallOperation", "ApplyPatchCallOperationCreateFile", "ApplyPatchCallOperationDeleteFile", "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", "ApplyPatchCallOutput", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", "McpListTools", "McpListToolsTool", "McpApprovalRequest", @@ -54,6 +69,8 @@ "McpCall", "CompactionTrigger", "ItemReference", + "Program", + "ProgramOutput", ] @@ -126,6 +143,24 @@ class ComputerCallOutput(BaseModel): """ +class FunctionCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Annotated[ + Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + class FunctionCallOutput(BaseModel): """The output of a function tool call.""" @@ -144,6 +179,9 @@ class FunctionCallOutput(BaseModel): Populated when this item is returned via API. """ + caller: Optional[FunctionCallOutputCaller] = None + """The execution context that produced this tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] = None """The status of the item. @@ -275,6 +313,23 @@ class ShellCallAction(BaseModel): """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" +class ShellCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Annotated[ + Union[ShellCallCallerDirect, ShellCallCallerProgram, None], PropertyInfo(discriminator="type") +] + ShellCallEnvironment: TypeAlias = Annotated[ Union[LocalEnvironment, ContainerReference, None], PropertyInfo(discriminator="type") ] @@ -298,6 +353,9 @@ class ShellCall(BaseModel): Populated when this item is returned via API. """ + caller: Optional[ShellCallCaller] = None + """The execution context that produced this tool call.""" + environment: Optional[ShellCallEnvironment] = None """The environment to execute the shell commands in.""" @@ -308,6 +366,24 @@ class ShellCall(BaseModel): """ +class ShellCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Annotated[ + Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + + class ShellCallOutput(BaseModel): """The streamed output items emitted by a shell tool call.""" @@ -329,6 +405,9 @@ class ShellCallOutput(BaseModel): Populated when this item is returned via API. """ + caller: Optional[ShellCallOutputCaller] = None + """The execution context that produced this tool call.""" + max_output_length: Optional[int] = None """ The maximum number of UTF-8 characters captured for this shell call's combined @@ -381,6 +460,24 @@ class ApplyPatchCallOperationUpdateFile(BaseModel): ] +class ApplyPatchCallCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram, None], PropertyInfo(discriminator="type") +] + + class ApplyPatchCall(BaseModel): """ A tool call representing a request to create, delete, or update files using diff patches. @@ -407,6 +504,27 @@ class ApplyPatchCall(BaseModel): Populated when this item is returned via API. """ + caller: Optional[ApplyPatchCallCaller] = None + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputCallerDirect(BaseModel): + type: Literal["direct"] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(BaseModel): + caller_id: str + """The call ID of the program item that produced this tool call.""" + + type: Literal["program"] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Annotated[ + Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram, None], PropertyInfo(discriminator="type") +] + class ApplyPatchCallOutput(BaseModel): """The streamed output emitted by an apply patch tool call.""" @@ -426,6 +544,9 @@ class ApplyPatchCallOutput(BaseModel): Populated when this item is returned via API. """ + caller: Optional[ApplyPatchCallOutputCaller] = None + """The execution context that produced this tool call.""" + output: Optional[str] = None """ Optional human-readable log text from the apply patch tool (e.g., patch results @@ -561,6 +682,40 @@ class ItemReference(BaseModel): """The type of item to reference. Always `item_reference`.""" +class Program(BaseModel): + id: str + """The unique ID of this program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The item type. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of this program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output.""" + + type: Literal["program_output"] + """The item type. Always `program_output`.""" + + ResponseInputItem: TypeAlias = Annotated[ Union[ EasyInputMessage, @@ -593,6 +748,8 @@ class ItemReference(BaseModel): ResponseCustomToolCall, CompactionTrigger, ItemReference, + Program, + ProgramOutput, ], PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 0b0d68cdbd..1d0d0b1b66 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -32,6 +32,9 @@ "ComputerCallOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", "ToolSearchCall", "AdditionalTools", "ImageGenerationCall", @@ -40,14 +43,26 @@ "LocalShellCallOutput", "ShellCall", "ShellCallAction", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", "ShellCallEnvironment", "ShellCallOutput", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", "ApplyPatchCall", "ApplyPatchCallOperation", "ApplyPatchCallOperationCreateFile", "ApplyPatchCallOperationDeleteFile", "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", "ApplyPatchCallOutput", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", "McpListTools", "McpListToolsTool", "McpApprovalRequest", @@ -55,6 +70,8 @@ "McpCall", "CompactionTrigger", "ItemReference", + "Program", + "ProgramOutput", ] @@ -127,6 +144,22 @@ class ComputerCallOutput(TypedDict, total=False): """ +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + class FunctionCallOutput(TypedDict, total=False): """The output of a function tool call.""" @@ -145,6 +178,9 @@ class FunctionCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the item. @@ -276,6 +312,21 @@ class ShellCallAction(TypedDict, total=False): """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + ShellCallEnvironment: TypeAlias = Union[LocalEnvironmentParam, ContainerReferenceParam] @@ -297,6 +348,9 @@ class ShellCall(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + environment: Optional[ShellCallEnvironment] """The environment to execute the shell commands in.""" @@ -307,6 +361,22 @@ class ShellCall(TypedDict, total=False): """ +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + class ShellCallOutput(TypedDict, total=False): """The streamed output items emitted by a shell tool call.""" @@ -328,6 +398,9 @@ class ShellCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + max_output_length: Optional[int] """ The maximum number of UTF-8 characters captured for this shell call's combined @@ -379,6 +452,22 @@ class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): ] +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + class ApplyPatchCall(TypedDict, total=False): """ A tool call representing a request to create, delete, or update files using diff patches. @@ -405,6 +494,25 @@ class ApplyPatchCall(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + class ApplyPatchCallOutput(TypedDict, total=False): """The streamed output emitted by an apply patch tool call.""" @@ -424,6 +532,9 @@ class ApplyPatchCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + output: Optional[str] """ Optional human-readable log text from the apply patch tool (e.g., patch results @@ -559,6 +670,40 @@ class ItemReference(TypedDict, total=False): """The type of item to reference. Always `item_reference`.""" +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + ResponseInputItemParam: TypeAlias = Union[ EasyInputMessageParam, Message, @@ -590,4 +735,6 @@ class ItemReference(TypedDict, total=False): ResponseCustomToolCallParam, CompactionTrigger, ItemReference, + Program, + ProgramOutput, ] diff --git a/src/openai/types/responses/response_input_param.py b/src/openai/types/responses/response_input_param.py index f45aa276c8..aa3219d0f5 100644 --- a/src/openai/types/responses/response_input_param.py +++ b/src/openai/types/responses/response_input_param.py @@ -33,6 +33,9 @@ "ComputerCallOutput", "ComputerCallOutputAcknowledgedSafetyCheck", "FunctionCallOutput", + "FunctionCallOutputCaller", + "FunctionCallOutputCallerDirect", + "FunctionCallOutputCallerProgram", "ToolSearchCall", "AdditionalTools", "ImageGenerationCall", @@ -41,14 +44,26 @@ "LocalShellCallOutput", "ShellCall", "ShellCallAction", + "ShellCallCaller", + "ShellCallCallerDirect", + "ShellCallCallerProgram", "ShellCallEnvironment", "ShellCallOutput", + "ShellCallOutputCaller", + "ShellCallOutputCallerDirect", + "ShellCallOutputCallerProgram", "ApplyPatchCall", "ApplyPatchCallOperation", "ApplyPatchCallOperationCreateFile", "ApplyPatchCallOperationDeleteFile", "ApplyPatchCallOperationUpdateFile", + "ApplyPatchCallCaller", + "ApplyPatchCallCallerDirect", + "ApplyPatchCallCallerProgram", "ApplyPatchCallOutput", + "ApplyPatchCallOutputCaller", + "ApplyPatchCallOutputCallerDirect", + "ApplyPatchCallOutputCallerProgram", "McpListTools", "McpListToolsTool", "McpApprovalRequest", @@ -56,6 +71,8 @@ "McpCall", "CompactionTrigger", "ItemReference", + "Program", + "ProgramOutput", ] @@ -128,6 +145,22 @@ class ComputerCallOutput(TypedDict, total=False): """ +class FunctionCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class FunctionCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +FunctionCallOutputCaller: TypeAlias = Union[FunctionCallOutputCallerDirect, FunctionCallOutputCallerProgram] + + class FunctionCallOutput(TypedDict, total=False): """The output of a function tool call.""" @@ -146,6 +179,9 @@ class FunctionCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[FunctionCallOutputCaller] + """The execution context that produced this tool call.""" + status: Optional[Literal["in_progress", "completed", "incomplete"]] """The status of the item. @@ -277,6 +313,21 @@ class ShellCallAction(TypedDict, total=False): """Maximum wall-clock time in milliseconds to allow the shell commands to run.""" +class ShellCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallCaller: TypeAlias = Union[ShellCallCallerDirect, ShellCallCallerProgram] + ShellCallEnvironment: TypeAlias = Union[LocalEnvironmentParam, ContainerReferenceParam] @@ -298,6 +349,9 @@ class ShellCall(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ShellCallCaller] + """The execution context that produced this tool call.""" + environment: Optional[ShellCallEnvironment] """The environment to execute the shell commands in.""" @@ -308,6 +362,22 @@ class ShellCall(TypedDict, total=False): """ +class ShellCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ShellCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ShellCallOutputCaller: TypeAlias = Union[ShellCallOutputCallerDirect, ShellCallOutputCallerProgram] + + class ShellCallOutput(TypedDict, total=False): """The streamed output items emitted by a shell tool call.""" @@ -329,6 +399,9 @@ class ShellCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ShellCallOutputCaller] + """The execution context that produced this tool call.""" + max_output_length: Optional[int] """ The maximum number of UTF-8 characters captured for this shell call's combined @@ -380,6 +453,22 @@ class ApplyPatchCallOperationUpdateFile(TypedDict, total=False): ] +class ApplyPatchCallCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallCaller: TypeAlias = Union[ApplyPatchCallCallerDirect, ApplyPatchCallCallerProgram] + + class ApplyPatchCall(TypedDict, total=False): """ A tool call representing a request to create, delete, or update files using diff patches. @@ -406,6 +495,25 @@ class ApplyPatchCall(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ApplyPatchCallCaller] + """The execution context that produced this tool call.""" + + +class ApplyPatchCallOutputCallerDirect(TypedDict, total=False): + type: Required[Literal["direct"]] + """The caller type. Always `direct`.""" + + +class ApplyPatchCallOutputCallerProgram(TypedDict, total=False): + caller_id: Required[str] + """The call ID of the program item that produced this tool call.""" + + type: Required[Literal["program"]] + """The caller type. Always `program`.""" + + +ApplyPatchCallOutputCaller: TypeAlias = Union[ApplyPatchCallOutputCallerDirect, ApplyPatchCallOutputCallerProgram] + class ApplyPatchCallOutput(TypedDict, total=False): """The streamed output emitted by an apply patch tool call.""" @@ -425,6 +533,9 @@ class ApplyPatchCallOutput(TypedDict, total=False): Populated when this item is returned via API. """ + caller: Optional[ApplyPatchCallOutputCaller] + """The execution context that produced this tool call.""" + output: Optional[str] """ Optional human-readable log text from the apply patch tool (e.g., patch results @@ -560,6 +671,40 @@ class ItemReference(TypedDict, total=False): """The type of item to reference. Always `item_reference`.""" +class Program(TypedDict, total=False): + id: Required[str] + """The unique ID of this program item.""" + + call_id: Required[str] + """The stable call ID of the program item.""" + + code: Required[str] + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: Required[str] + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Required[Literal["program"]] + """The item type. Always `program`.""" + + +class ProgramOutput(TypedDict, total=False): + id: Required[str] + """The unique ID of this program output item.""" + + call_id: Required[str] + """The call ID of the program item.""" + + result: Required[str] + """The result produced by the program item.""" + + status: Required[Literal["completed", "incomplete"]] + """The terminal status of the program output.""" + + type: Required[Literal["program_output"]] + """The item type. Always `program_output`.""" + + ResponseInputItemParam: TypeAlias = Union[ EasyInputMessageParam, Message, @@ -591,6 +736,8 @@ class ItemReference(TypedDict, total=False): ResponseCustomToolCallParam, CompactionTrigger, ItemReference, + Program, + ProgramOutput, ] ResponseInputParam: TypeAlias = List[ResponseInputItemParam] diff --git a/src/openai/types/responses/response_input_text.py b/src/openai/types/responses/response_input_text.py index 1e06ba71f3..05244edc25 100644 --- a/src/openai/types/responses/response_input_text.py +++ b/src/openai/types/responses/response_input_text.py @@ -1,10 +1,21 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["ResponseInputText"] +__all__ = ["ResponseInputText", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ResponseInputText(BaseModel): @@ -15,3 +26,10 @@ class ResponseInputText(BaseModel): type: Literal["input_text"] """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_text_content.py b/src/openai/types/responses/response_input_text_content.py index 66dbb8b0d0..bd43a5e759 100644 --- a/src/openai/types/responses/response_input_text_content.py +++ b/src/openai/types/responses/response_input_text_content.py @@ -1,10 +1,21 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel -__all__ = ["ResponseInputTextContent"] +__all__ = ["ResponseInputTextContent", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(BaseModel): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Literal["explicit"] + """The breakpoint mode. Always `explicit`.""" class ResponseInputTextContent(BaseModel): @@ -15,3 +26,10 @@ class ResponseInputTextContent(BaseModel): type: Literal["input_text"] """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] = None + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_text_content_param.py b/src/openai/types/responses/response_input_text_content_param.py index 013f22d0df..e1956b8f06 100644 --- a/src/openai/types/responses/response_input_text_content_param.py +++ b/src/openai/types/responses/response_input_text_content_param.py @@ -2,9 +2,20 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Literal, Required, TypedDict -__all__ = ["ResponseInputTextContentParam"] +__all__ = ["ResponseInputTextContentParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" class ResponseInputTextContentParam(TypedDict, total=False): @@ -15,3 +26,10 @@ class ResponseInputTextContentParam(TypedDict, total=False): type: Required[Literal["input_text"]] """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: Optional[PromptCacheBreakpoint] + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_input_text_param.py b/src/openai/types/responses/response_input_text_param.py index e1a2976e2e..d82fd8ed1f 100644 --- a/src/openai/types/responses/response_input_text_param.py +++ b/src/openai/types/responses/response_input_text_param.py @@ -4,7 +4,17 @@ from typing_extensions import Literal, Required, TypedDict -__all__ = ["ResponseInputTextParam"] +__all__ = ["ResponseInputTextParam", "PromptCacheBreakpoint"] + + +class PromptCacheBreakpoint(TypedDict, total=False): + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; the boundary is not rounded to a token block. + """ + + mode: Required[Literal["explicit"]] + """The breakpoint mode. Always `explicit`.""" class ResponseInputTextParam(TypedDict, total=False): @@ -15,3 +25,10 @@ class ResponseInputTextParam(TypedDict, total=False): type: Required[Literal["input_text"]] """The type of the input item. Always `input_text`.""" + + prompt_cache_breakpoint: PromptCacheBreakpoint + """Marks the exact end of a reusable prompt prefix. + + The breakpoint inherits its TTL from the request's `prompt_cache_options.ttl`; + the boundary is not rounded to a token block. + """ diff --git a/src/openai/types/responses/response_item.py b/src/openai/types/responses/response_item.py index eb2cd44192..9ae6c18fc5 100644 --- a/src/openai/types/responses/response_item.py +++ b/src/openai/types/responses/response_item.py @@ -29,6 +29,8 @@ __all__ = [ "ResponseItem", "AdditionalTools", + "Program", + "ProgramOutput", "ImageGenerationCall", "LocalShellCall", "LocalShellCallAction", @@ -55,6 +57,40 @@ class AdditionalTools(BaseModel): """The type of the item. Always `additional_tools`.""" +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + class ImageGenerationCall(BaseModel): """An image generation request made by the model.""" @@ -253,6 +289,8 @@ class McpCall(BaseModel): ResponseToolSearchOutputItem, AdditionalTools, ResponseReasoningItem, + Program, + ProgramOutput, ResponseCompactionItem, ImageGenerationCall, ResponseCodeInterpreterToolCall, diff --git a/src/openai/types/responses/response_output_item.py b/src/openai/types/responses/response_output_item.py index ee7057348b..046a0a3cfb 100644 --- a/src/openai/types/responses/response_output_item.py +++ b/src/openai/types/responses/response_output_item.py @@ -27,6 +27,8 @@ __all__ = [ "ResponseOutputItem", + "Program", + "ProgramOutput", "AdditionalTools", "ImageGenerationCall", "LocalShellCall", @@ -40,6 +42,40 @@ ] +class Program(BaseModel): + id: str + """The unique ID of the program item.""" + + call_id: str + """The stable call ID of the program item.""" + + code: str + """The JavaScript source executed by programmatic tool calling.""" + + fingerprint: str + """Opaque program replay fingerprint that must be round-tripped.""" + + type: Literal["program"] + """The type of the item. Always `program`.""" + + +class ProgramOutput(BaseModel): + id: str + """The unique ID of the program output item.""" + + call_id: str + """The call ID of the program item.""" + + result: str + """The result produced by the program item.""" + + status: Literal["completed", "incomplete"] + """The terminal status of the program output item.""" + + type: Literal["program_output"] + """The type of the item. Always `program_output`.""" + + class AdditionalTools(BaseModel): id: str """The unique ID of the additional tools item.""" @@ -248,6 +284,8 @@ class McpApprovalResponse(BaseModel): ResponseComputerToolCall, ResponseComputerToolCallOutputItem, ResponseReasoningItem, + Program, + ProgramOutput, ResponseToolSearchCall, ResponseToolSearchOutputItem, AdditionalTools, diff --git a/src/openai/types/responses/response_reasoning_summary_part_done_event.py b/src/openai/types/responses/response_reasoning_summary_part_done_event.py index 48f3f684e8..184ba698d0 100644 --- a/src/openai/types/responses/response_reasoning_summary_part_done_event.py +++ b/src/openai/types/responses/response_reasoning_summary_part_done_event.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from typing import Optional from typing_extensions import Literal from ..._models import BaseModel @@ -37,3 +38,10 @@ class ResponseReasoningSummaryPartDoneEvent(BaseModel): type: Literal["response.reasoning_summary_part.done"] """The type of the event. Always `response.reasoning_summary_part.done`.""" + + status: Optional[Literal["incomplete"]] = None + """The completion status of the summary part. + + Omitted when the part completed normally and set to `incomplete` when generation + was interrupted. + """ diff --git a/src/openai/types/responses/response_usage.py b/src/openai/types/responses/response_usage.py index d4b739c598..7a63daafa7 100644 --- a/src/openai/types/responses/response_usage.py +++ b/src/openai/types/responses/response_usage.py @@ -8,6 +8,9 @@ class InputTokensDetails(BaseModel): """A detailed breakdown of the input tokens.""" + cache_write_tokens: int + """The number of input tokens that were written to the cache.""" + cached_tokens: int """The number of tokens that were retrieved from the cache. diff --git a/src/openai/types/responses/responses_client_event.py b/src/openai/types/responses/responses_client_event.py index df33c019f1..44606d8201 100644 --- a/src/openai/types/responses/responses_client_event.py +++ b/src/openai/types/responses/responses_client_event.py @@ -22,7 +22,19 @@ from .tool_choice_apply_patch import ToolChoiceApplyPatch from .response_conversation_param import ResponseConversationParam -__all__ = ["ResponsesClientEvent", "ContextManagement", "Conversation", "Moderation", "StreamOptions", "ToolChoice"] +__all__ = [ + "ResponsesClientEvent", + "ContextManagement", + "Conversation", + "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", + "StreamOptions", + "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", +] class ContextManagement(BaseModel): @@ -36,6 +48,28 @@ class ContextManagement(BaseModel): Conversation: TypeAlias = Union[str, ResponseConversationParam, None] +class ModerationPolicyInput(BaseModel): + """The moderation policy for the response input.""" + + mode: Literal["score", "block"] + + +class ModerationPolicyOutput(BaseModel): + """The moderation policy for the response output.""" + + mode: Literal["score", "block"] + + +class ModerationPolicy(BaseModel): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] = None + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] = None + """The moderation policy for the response output.""" + + class Moderation(BaseModel): """Configuration for running moderation on the input and output of this response.""" @@ -45,6 +79,33 @@ class Moderation(BaseModel): 'omni-moderation-latest'. """ + policy: Optional[ModerationPolicy] = None + """The policy to apply to moderated response input and output.""" + + +class PromptCacheOptions(BaseModel): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Optional[Literal["implicit", "explicit"]] = None + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Optional[Literal["30m"]] = None + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + class StreamOptions(BaseModel): """Options for streaming responses. Only set this when you set `stream: true`.""" @@ -61,6 +122,11 @@ class StreamOptions(BaseModel): """ +class ToolChoiceSpecificProgrammaticToolCallingParam(BaseModel): + type: Literal["programmatic_tool_calling"] + """The tool to call. Always `programmatic_tool_calling`.""" + + ToolChoice: TypeAlias = Union[ ToolChoiceOptions, ToolChoiceAllowed, @@ -68,6 +134,7 @@ class StreamOptions(BaseModel): ToolChoiceFunction, ToolChoiceMcp, ToolChoiceCustom, + ToolChoiceSpecificProgrammaticToolCallingParam, ToolChoiceApplyPatch, ToolChoiceShell, ] @@ -197,13 +264,31 @@ class ResponsesClientEvent(BaseModel): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_options: Optional[PromptCacheOptions] = None + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] = None - """The retention policy for the prompt cache. + """Deprecated. Use `prompt_cache_options.ttl` instead. - Set to `24h` to enable extended prompt caching, which keeps cached prefixes - active for longer, up to a maximum of 24 hours. + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py index 55aeafc33d..818248b818 100644 --- a/src/openai/types/responses/responses_client_event_param.py +++ b/src/openai/types/responses/responses_client_event_param.py @@ -28,8 +28,13 @@ "ContextManagement", "Conversation", "Moderation", + "ModerationPolicy", + "ModerationPolicyInput", + "ModerationPolicyOutput", + "PromptCacheOptions", "StreamOptions", "ToolChoice", + "ToolChoiceSpecificProgrammaticToolCallingParam", ] @@ -44,6 +49,28 @@ class ContextManagement(TypedDict, total=False): Conversation: TypeAlias = Union[str, ResponseConversationParamParam] +class ModerationPolicyInput(TypedDict, total=False): + """The moderation policy for the response input.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicyOutput(TypedDict, total=False): + """The moderation policy for the response output.""" + + mode: Required[Literal["score", "block"]] + + +class ModerationPolicy(TypedDict, total=False): + """The policy to apply to moderated response input and output.""" + + input: Optional[ModerationPolicyInput] + """The moderation policy for the response input.""" + + output: Optional[ModerationPolicyOutput] + """The moderation policy for the response output.""" + + class Moderation(TypedDict, total=False): """Configuration for running moderation on the input and output of this response.""" @@ -53,6 +80,33 @@ class Moderation(TypedDict, total=False): 'omni-moderation-latest'. """ + policy: Optional[ModerationPolicy] + """The policy to apply to moderated response input and output.""" + + +class PromptCacheOptions(TypedDict, total=False): + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically chooses one implicit cache breakpoint. You can add explicit breakpoints to content blocks with `prompt_cache_breakpoint`. Each request can write up to four breakpoints. For cache matching, OpenAI considers up to the latest 80 breakpoints in the conversation, without a content-block lookback limit. Set `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to `30m`, which is currently the only supported value. See the [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) for current details. + """ + + mode: Literal["implicit", "explicit"] + """Controls whether OpenAI automatically creates an implicit cache breakpoint. + + Defaults to `implicit`. With `implicit`, OpenAI creates one implicit breakpoint + and writes up to the latest three explicit breakpoints in the request. With + `explicit`, OpenAI does not create an implicit breakpoint and writes up to the + latest four explicit breakpoints. If there are no explicit breakpoints, the + request does not use prompt caching. + """ + + ttl: Literal["30m"] + """ + The minimum lifetime applied to every implicit and explicit cache breakpoint + written by the request. Defaults to `30m`, which is currently the only supported + value. The backend may retain cache entries for longer. + """ + class StreamOptions(TypedDict, total=False): """Options for streaming responses. Only set this when you set `stream: true`.""" @@ -69,6 +123,11 @@ class StreamOptions(TypedDict, total=False): """ +class ToolChoiceSpecificProgrammaticToolCallingParam(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The tool to call. Always `programmatic_tool_calling`.""" + + ToolChoice: TypeAlias = Union[ ToolChoiceOptions, ToolChoiceAllowedParam, @@ -76,6 +135,7 @@ class StreamOptions(TypedDict, total=False): ToolChoiceFunctionParam, ToolChoiceMcpParam, ToolChoiceCustomParam, + ToolChoiceSpecificProgrammaticToolCallingParam, ToolChoiceApplyPatchParam, ToolChoiceShellParam, ] @@ -205,13 +265,31 @@ class ResponsesClientEventParam(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/prompt-caching). """ + prompt_cache_options: PromptCacheOptions + """Options for prompt caching. + + Supported for `gpt-5.6` and later models. By default, OpenAI automatically + chooses one implicit cache breakpoint. You can add explicit breakpoints to + content blocks with `prompt_cache_breakpoint`. Each request can write up to four + breakpoints. For cache matching, OpenAI considers up to the latest 80 + breakpoints in the conversation, without a content-block lookback limit. Set + `mode` to `explicit` to disable the implicit breakpoint. The `ttl` defaults to + `30m`, which is currently the only supported value. See the + [prompt caching guide](https://platform.openai.com/docs/guides/prompt-caching) + for current details. + """ + prompt_cache_retention: Optional[Literal["in_memory", "24h"]] - """The retention policy for the prompt cache. + """Deprecated. Use `prompt_cache_options.ttl` instead. - Set to `24h` to enable extended prompt caching, which keeps cached prefixes - active for longer, up to a maximum of 24 hours. + The retention policy for the prompt cache. Set to `24h` to enable extended + prompt caching, which keeps cached prefixes active for longer, up to a maximum + of 24 hours. [Learn more](https://platform.openai.com/docs/guides/prompt-caching#prompt-cache-retention). - For `gpt-5.5`, `gpt-5.5-pro`, and future models, only `24h` is supported. + This field expresses a maximum retention policy, while + `prompt_cache_options.ttl` expresses a minimum cache lifetime. The two fields + are independent and do not interact. For `gpt-5.5`, `gpt-5.5-pro`, and future + models, only `24h` is supported. For older models that support both `in_memory` and `24h`, the default depends on your organization's data retention policy: diff --git a/src/openai/types/responses/tool.py b/src/openai/types/responses/tool.py index 33bfcd41ff..8b83ad03a9 100644 --- a/src/openai/types/responses/tool.py +++ b/src/openai/types/responses/tool.py @@ -34,6 +34,7 @@ "CodeInterpreterContainer", "CodeInterpreterContainerCodeInterpreterToolAuto", "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", "ImageGeneration", "ImageGenerationInputImageMask", "LocalShell", @@ -121,6 +122,9 @@ class Mcp(BaseModel): type: Literal["mcp"] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + allowed_tools: Optional[McpAllowedTools] = None """List of allowed tool names or a filter object.""" @@ -229,6 +233,14 @@ class CodeInterpreter(BaseModel): type: Literal["code_interpreter"] """The type of the code interpreter tool. Always `code_interpreter`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] = None + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(BaseModel): + type: Literal["programmatic_tool_calling"] + """The type of the tool. Always `programmatic_tool_calling`.""" + class ImageGenerationInputImageMask(BaseModel): """Optional mask for inpainting. @@ -353,6 +365,7 @@ class LocalShell(BaseModel): WebSearchTool, Mcp, CodeInterpreter, + ProgrammaticToolCalling, ImageGeneration, LocalShell, FunctionShellTool, diff --git a/src/openai/types/responses/tool_param.py b/src/openai/types/responses/tool_param.py index a2f7272c36..4d46891007 100644 --- a/src/openai/types/responses/tool_param.py +++ b/src/openai/types/responses/tool_param.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, Union, Optional +from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from . import web_search_tool_param @@ -35,6 +35,7 @@ "CodeInterpreterContainer", "CodeInterpreterContainerCodeInterpreterToolAuto", "CodeInterpreterContainerCodeInterpreterToolAutoNetworkPolicy", + "ProgrammaticToolCalling", "ImageGeneration", "ImageGenerationInputImageMask", "LocalShell", @@ -123,6 +124,9 @@ class Mcp(TypedDict, total=False): type: Required[Literal["mcp"]] """The type of the MCP tool. Always `mcp`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + allowed_tools: Optional[McpAllowedTools] """List of allowed tool names or a filter object.""" @@ -229,6 +233,14 @@ class CodeInterpreter(TypedDict, total=False): type: Required[Literal["code_interpreter"]] """The type of the code interpreter tool. Always `code_interpreter`.""" + allowed_callers: Optional[List[Literal["direct", "programmatic"]]] + """The tool invocation context(s).""" + + +class ProgrammaticToolCalling(TypedDict, total=False): + type: Required[Literal["programmatic_tool_calling"]] + """The type of the tool. Always `programmatic_tool_calling`.""" + class ImageGenerationInputImageMask(TypedDict, total=False): """Optional mask for inpainting. @@ -351,6 +363,7 @@ class LocalShell(TypedDict, total=False): WebSearchToolParam, Mcp, CodeInterpreter, + ProgrammaticToolCalling, ImageGeneration, LocalShell, FunctionShellToolParam, diff --git a/src/openai/types/shared/chat_model.py b/src/openai/types/shared/chat_model.py index 501a22a8bd..5c705909d5 100644 --- a/src/openai/types/shared/chat_model.py +++ b/src/openai/types/shared/chat_model.py @@ -5,6 +5,9 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano", diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 6a7b3e4b7d..7dddbb9b44 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Union, Optional from typing_extensions import Literal from ..._models import BaseModel @@ -24,20 +24,14 @@ class Reasoning(BaseModel): """ effort: Optional[ReasoningEffort] = None - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] = None @@ -48,6 +42,12 @@ class Reasoning(BaseModel): `concise`, or `detailed`. """ + mode: Union[str, Literal["standard", "pro"], None] = None + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + summary: Optional[Literal["auto", "concise", "detailed"]] = None """A summary of the reasoning performed by the model. diff --git a/src/openai/types/shared/reasoning_effort.py b/src/openai/types/shared/reasoning_effort.py index 24d8516424..0ecf058596 100644 --- a/src/openai/types/shared/reasoning_effort.py +++ b/src/openai/types/shared/reasoning_effort.py @@ -5,4 +5,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] diff --git a/src/openai/types/shared_params/chat_model.py b/src/openai/types/shared_params/chat_model.py index 17eaacd905..082ece0918 100644 --- a/src/openai/types/shared_params/chat_model.py +++ b/src/openai/types/shared_params/chat_model.py @@ -7,6 +7,9 @@ __all__ = ["ChatModel"] ChatModel: TypeAlias = Literal[ + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.4", "gpt-5.4-mini", "gpt-5.4-nano", diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 86ebc259f1..70cf624f0e 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Optional +from typing import Union, Optional from typing_extensions import Literal, TypedDict from ..shared.reasoning_effort import ReasoningEffort @@ -25,20 +25,14 @@ class Reasoning(TypedDict, total=False): """ effort: Optional[ReasoningEffort] - """ - Constrains effort on reasoning for - [reasoning models](https://platform.openai.com/docs/guides/reasoning). Currently - supported values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. - Reducing reasoning effort can result in faster responses and fewer tokens used - on reasoning in a response. - - - `gpt-5.1` defaults to `none`, which does not perform reasoning. The supported - reasoning values for `gpt-5.1` are `none`, `low`, `medium`, and `high`. Tool - calls are supported for all reasoning values in gpt-5.1. - - All models before `gpt-5.1` default to `medium` reasoning effort, and do not - support `none`. - - The `gpt-5-pro` model defaults to (and only supports) `high` reasoning effort. - - `xhigh` is supported for all models after `gpt-5.1-codex-max`. + """Constrains effort on reasoning for reasoning models. + + Currently supported values are `none`, `minimal`, `low`, `medium`, `high`, + `xhigh`, and `max`. Reducing reasoning effort can result in faster responses and + fewer tokens used on reasoning in a response. Not all reasoning models support + every value. See the + [reasoning guide](https://platform.openai.com/docs/guides/reasoning) for + model-specific support. """ generate_summary: Optional[Literal["auto", "concise", "detailed"]] @@ -49,6 +43,12 @@ class Reasoning(TypedDict, total=False): `concise`, or `detailed`. """ + mode: Union[str, Literal["standard", "pro"]] + """Controls the reasoning execution mode for the request. + + When returned on a response, this is the effective execution mode. + """ + summary: Optional[Literal["auto", "concise", "detailed"]] """A summary of the reasoning performed by the model. diff --git a/src/openai/types/shared_params/reasoning_effort.py b/src/openai/types/shared_params/reasoning_effort.py index 8518c2b141..fede2a25a9 100644 --- a/src/openai/types/shared_params/reasoning_effort.py +++ b/src/openai/types/shared_params/reasoning_effort.py @@ -7,4 +7,4 @@ __all__ = ["ReasoningEffort"] -ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh"]] +ReasoningEffort: TypeAlias = Optional[Literal["none", "minimal", "low", "medium", "high", "xhigh", "max"]] diff --git a/src/openai/types/webhooks/__init__.py b/src/openai/types/webhooks/__init__.py index 8b9e55653b..dcb4f3dec4 100644 --- a/src/openai/types/webhooks/__init__.py +++ b/src/openai/types/webhooks/__init__.py @@ -22,3 +22,6 @@ from .fine_tuning_job_succeeded_webhook_event import ( FineTuningJobSucceededWebhookEvent as FineTuningJobSucceededWebhookEvent, ) +from .safety_identifier_blocked_webhook_event import ( + SafetyIdentifierBlockedWebhookEvent as SafetyIdentifierBlockedWebhookEvent, +) diff --git a/src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py b/src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py new file mode 100644 index 0000000000..04472727ac --- /dev/null +++ b/src/openai/types/webhooks/safety_identifier_blocked_webhook_event.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SafetyIdentifierBlockedWebhookEvent", "Data"] + + +class Data(BaseModel): + """Event data payload.""" + + safety_category: str + """The safety category that triggered the block, such as `bio` or `cyber`.""" + + safety_identifier: str + """The stable safety identifier associated with the blocked request.""" + + model: Optional[str] = None + """The model used for the blocked request, if available.""" + + project_id: Optional[str] = None + """The project associated with the blocked request, if available.""" + + request_id: Optional[str] = None + """The OpenAI request ID for the blocked request, if available.""" + + +class SafetyIdentifierBlockedWebhookEvent(BaseModel): + """Sent when a request associated with a safety identifier has been blocked.""" + + id: str + """The unique ID of the event.""" + + created_at: int + """The Unix timestamp (in seconds) of when the request was blocked.""" + + data: Data + """Event data payload.""" + + type: Literal["safety_identifier.blocked"] + """The type of the event. Always `safety_identifier.blocked`.""" + + object: Optional[Literal["event"]] = None + """The object of the event. Always `event`.""" diff --git a/src/openai/types/webhooks/unwrap_webhook_event.py b/src/openai/types/webhooks/unwrap_webhook_event.py index 952383c049..20b77c493f 100644 --- a/src/openai/types/webhooks/unwrap_webhook_event.py +++ b/src/openai/types/webhooks/unwrap_webhook_event.py @@ -19,6 +19,7 @@ from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent from .fine_tuning_job_cancelled_webhook_event import FineTuningJobCancelledWebhookEvent from .fine_tuning_job_succeeded_webhook_event import FineTuningJobSucceededWebhookEvent +from .safety_identifier_blocked_webhook_event import SafetyIdentifierBlockedWebhookEvent __all__ = ["UnwrapWebhookEvent"] @@ -39,6 +40,7 @@ ResponseCompletedWebhookEvent, ResponseFailedWebhookEvent, ResponseIncompleteWebhookEvent, + SafetyIdentifierBlockedWebhookEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/tests/api_resources/beta/responses/__init__.py b/tests/api_resources/beta/responses/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/beta/responses/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/beta/responses/test_input_items.py b/tests/api_resources/beta/responses/test_input_items.py new file mode 100644 index 0000000000..ff13ce610a --- /dev/null +++ b/tests/api_resources/beta/responses/test_input_items.py @@ -0,0 +1,125 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.pagination import SyncCursorPage, AsyncCursorPage +from openai.types.beta import BetaResponseItem + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInputItems: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_list(self, client: OpenAI) -> None: + input_item = client.beta.responses.input_items.list( + response_id="response_id", + ) + assert_matches_type(SyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: OpenAI) -> None: + input_item = client.beta.responses.input_items.list( + response_id="response_id", + after="after", + include=["file_search_call.results"], + limit=0, + order="asc", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(SyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: OpenAI) -> None: + response = client.beta.responses.input_items.with_raw_response.list( + response_id="response_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_item = response.parse() + assert_matches_type(SyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: OpenAI) -> None: + with client.beta.responses.input_items.with_streaming_response.list( + response_id="response_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_item = response.parse() + assert_matches_type(SyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.beta.responses.input_items.with_raw_response.list( + response_id="", + ) + + +class TestAsyncInputItems: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenAI) -> None: + input_item = await async_client.beta.responses.input_items.list( + response_id="response_id", + ) + assert_matches_type(AsyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> None: + input_item = await async_client.beta.responses.input_items.list( + response_id="response_id", + after="after", + include=["file_search_call.results"], + limit=0, + order="asc", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(AsyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.input_items.with_raw_response.list( + response_id="response_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_item = response.parse() + assert_matches_type(AsyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.input_items.with_streaming_response.list( + response_id="response_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_item = await response.parse() + assert_matches_type(AsyncCursorPage[BetaResponseItem], input_item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.beta.responses.input_items.with_raw_response.list( + response_id="", + ) diff --git a/tests/api_resources/beta/responses/test_input_tokens.py b/tests/api_resources/beta/responses/test_input_tokens.py new file mode 100644 index 0000000000..5140cb4d43 --- /dev/null +++ b/tests/api_resources/beta/responses/test_input_tokens.py @@ -0,0 +1,152 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.beta.responses import InputTokenCountResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestInputTokens: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_count(self, client: OpenAI) -> None: + input_token = client.beta.responses.input_tokens.count() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + def test_method_count_with_all_params(self, client: OpenAI) -> None: + input_token = client.beta.responses.input_tokens.count( + conversation="string", + input="string", + instructions="instructions", + model="model", + parallel_tool_calls=True, + personality="friendly", + previous_response_id="resp_123", + reasoning={ + "context": "auto", + "effort": "none", + "generate_summary": "auto", + "mode": "standard", + "summary": "auto", + }, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "allowed_callers": ["direct"], + "defer_loading": True, + "description": "description", + "output_schema": {"foo": "bar"}, + } + ], + truncation="auto", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + def test_raw_response_count(self, client: OpenAI) -> None: + response = client.beta.responses.input_tokens.with_raw_response.count() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_token = response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + def test_streaming_response_count(self, client: OpenAI) -> None: + with client.beta.responses.input_tokens.with_streaming_response.count() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_token = response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncInputTokens: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_count(self, async_client: AsyncOpenAI) -> None: + input_token = await async_client.beta.responses.input_tokens.count() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> None: + input_token = await async_client.beta.responses.input_tokens.count( + conversation="string", + input="string", + instructions="instructions", + model="model", + parallel_tool_calls=True, + personality="friendly", + previous_response_id="resp_123", + reasoning={ + "context": "auto", + "effort": "none", + "generate_summary": "auto", + "mode": "standard", + "summary": "auto", + }, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "allowed_callers": ["direct"], + "defer_loading": True, + "description": "description", + "output_schema": {"foo": "bar"}, + } + ], + truncation="auto", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + async def test_raw_response_count(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.input_tokens.with_raw_response.count() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + input_token = response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + @parametrize + async def test_streaming_response_count(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.input_tokens.with_streaming_response.count() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + input_token = await response.parse() + assert_matches_type(InputTokenCountResponse, input_token, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/beta/test_responses.py b/tests/api_resources/beta/test_responses.py new file mode 100644 index 0000000000..a01ad5675a --- /dev/null +++ b/tests/api_resources/beta/test_responses.py @@ -0,0 +1,945 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.beta import ( + BetaResponse, + BetaCompactedResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestResponses: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create_overload_1(self, client: OpenAI) -> None: + response = client.beta.responses.create() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: + response = client.beta.responses.create( + background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], + conversation="string", + include=["file_search_call.results"], + input="string", + instructions="instructions", + max_output_tokens=16, + max_tool_calls=0, + metadata={"foo": "string"}, + model="gpt-5.1", + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, + multi_agent={ + "enabled": True, + "max_concurrent_subagents": 1, + }, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + prompt={ + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, + prompt_cache_retention="in_memory", + reasoning={ + "context": "auto", + "effort": "none", + "generate_summary": "auto", + "mode": "standard", + "summary": "auto", + }, + safety_identifier="safety-identifier-1234", + service_tier="auto", + store=True, + stream=False, + stream_options={"include_obfuscation": True}, + temperature=1, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "allowed_callers": ["direct"], + "defer_loading": True, + "description": "description", + "output_schema": {"foo": "bar"}, + } + ], + top_logprobs=0, + top_p=1, + truncation="auto", + user="user-1234", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_raw_response_create_overload_1(self, client: OpenAI) -> None: + http_response = client.beta.responses.with_raw_response.create() + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_streaming_response_create_overload_1(self, client: OpenAI) -> None: + with client.beta.responses.with_streaming_response.create() as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_method_create_overload_2(self, client: OpenAI) -> None: + response_stream = client.beta.responses.create( + stream=True, + ) + response_stream.response.close() + + @parametrize + def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: + response_stream = client.beta.responses.create( + stream=True, + background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], + conversation="string", + include=["file_search_call.results"], + input="string", + instructions="instructions", + max_output_tokens=16, + max_tool_calls=0, + metadata={"foo": "string"}, + model="gpt-5.1", + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, + multi_agent={ + "enabled": True, + "max_concurrent_subagents": 1, + }, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + prompt={ + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, + prompt_cache_retention="in_memory", + reasoning={ + "context": "auto", + "effort": "none", + "generate_summary": "auto", + "mode": "standard", + "summary": "auto", + }, + safety_identifier="safety-identifier-1234", + service_tier="auto", + store=True, + stream_options={"include_obfuscation": True}, + temperature=1, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "allowed_callers": ["direct"], + "defer_loading": True, + "description": "description", + "output_schema": {"foo": "bar"}, + } + ], + top_logprobs=0, + top_p=1, + truncation="auto", + user="user-1234", + betas=["responses_multi_agent=v1"], + ) + response_stream.response.close() + + @parametrize + def test_raw_response_create_overload_2(self, client: OpenAI) -> None: + response = client.beta.responses.with_raw_response.create( + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_create_overload_2(self, client: OpenAI) -> None: + with client.beta.responses.with_streaming_response.create( + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve_overload_1(self, client: OpenAI) -> None: + response = client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params_overload_1(self, client: OpenAI) -> None: + response = client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + include=["file_search_call.results"], + include_obfuscation=True, + starting_after=0, + stream=False, + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_raw_response_retrieve_overload_1(self, client: OpenAI) -> None: + http_response = client.beta.responses.with_raw_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_streaming_response_retrieve_overload_1(self, client: OpenAI) -> None: + with client.beta.responses.with_streaming_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_path_params_retrieve_overload_1(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.beta.responses.with_raw_response.retrieve( + response_id="", + ) + + @parametrize + def test_method_retrieve_overload_2(self, client: OpenAI) -> None: + response_stream = client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + ) + response_stream.response.close() + + @parametrize + def test_method_retrieve_with_all_params_overload_2(self, client: OpenAI) -> None: + response_stream = client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + include=["file_search_call.results"], + include_obfuscation=True, + starting_after=0, + betas=["responses_multi_agent=v1"], + ) + response_stream.response.close() + + @parametrize + def test_raw_response_retrieve_overload_2(self, client: OpenAI) -> None: + response = client.beta.responses.with_raw_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + stream.close() + + @parametrize + def test_streaming_response_retrieve_overload_2(self, client: OpenAI) -> None: + with client.beta.responses.with_streaming_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = response.parse() + stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve_overload_2(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.beta.responses.with_raw_response.retrieve( + response_id="", + stream=True, + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + response = client.beta.responses.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert response is None + + @parametrize + def test_method_delete_with_all_params(self, client: OpenAI) -> None: + response = client.beta.responses.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + betas=["responses_multi_agent=v1"], + ) + assert response is None + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + http_response = client.beta.responses.with_raw_response.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert response is None + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.beta.responses.with_streaming_response.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert response is None + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.beta.responses.with_raw_response.delete( + response_id="", + ) + + @parametrize + def test_method_cancel(self, client: OpenAI) -> None: + response = client.beta.responses.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_method_cancel_with_all_params(self, client: OpenAI) -> None: + response = client.beta.responses.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_raw_response_cancel(self, client: OpenAI) -> None: + http_response = client.beta.responses.with_raw_response.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + def test_streaming_response_cancel(self, client: OpenAI) -> None: + with client.beta.responses.with_streaming_response.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + def test_path_params_cancel(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + client.beta.responses.with_raw_response.cancel( + response_id="", + ) + + @parametrize + def test_method_compact(self, client: OpenAI) -> None: + response = client.beta.responses.compact( + model="gpt-5.6-sol", + ) + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + @parametrize + def test_method_compact_with_all_params(self, client: OpenAI) -> None: + response = client.beta.responses.compact( + model="gpt-5.6-sol", + input="string", + instructions="instructions", + previous_response_id="resp_123", + prompt_cache_key="prompt_cache_key", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, + prompt_cache_retention="in_memory", + service_tier="auto", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + @parametrize + def test_raw_response_compact(self, client: OpenAI) -> None: + http_response = client.beta.responses.with_raw_response.compact( + model="gpt-5.6-sol", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + @parametrize + def test_streaming_response_compact(self, client: OpenAI) -> None: + with client.beta.responses.with_streaming_response.compact( + model="gpt-5.6-sol", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = http_response.parse() + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + +class TestAsyncResponses: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.create() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.create( + background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], + conversation="string", + include=["file_search_call.results"], + input="string", + instructions="instructions", + max_output_tokens=16, + max_tool_calls=0, + metadata={"foo": "string"}, + model="gpt-5.1", + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, + multi_agent={ + "enabled": True, + "max_concurrent_subagents": 1, + }, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + prompt={ + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, + prompt_cache_retention="in_memory", + reasoning={ + "context": "auto", + "effort": "none", + "generate_summary": "auto", + "mode": "standard", + "summary": "auto", + }, + safety_identifier="safety-identifier-1234", + service_tier="auto", + store=True, + stream=False, + stream_options={"include_obfuscation": True}, + temperature=1, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "allowed_callers": ["direct"], + "defer_loading": True, + "description": "description", + "output_schema": {"foo": "bar"}, + } + ], + top_logprobs=0, + top_p=1, + truncation="auto", + user="user-1234", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.beta.responses.with_raw_response.create() + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.with_streaming_response.create() as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response_stream = await async_client.beta.responses.create( + stream=True, + ) + await response_stream.response.aclose() + + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + response_stream = await async_client.beta.responses.create( + stream=True, + background=True, + context_management=[ + { + "type": "type", + "compact_threshold": 1000, + } + ], + conversation="string", + include=["file_search_call.results"], + input="string", + instructions="instructions", + max_output_tokens=16, + max_tool_calls=0, + metadata={"foo": "string"}, + model="gpt-5.1", + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, + multi_agent={ + "enabled": True, + "max_concurrent_subagents": 1, + }, + parallel_tool_calls=True, + previous_response_id="previous_response_id", + prompt={ + "id": "id", + "variables": {"foo": "string"}, + "version": "version", + }, + prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, + prompt_cache_retention="in_memory", + reasoning={ + "context": "auto", + "effort": "none", + "generate_summary": "auto", + "mode": "standard", + "summary": "auto", + }, + safety_identifier="safety-identifier-1234", + service_tier="auto", + store=True, + stream_options={"include_obfuscation": True}, + temperature=1, + text={ + "format": {"type": "text"}, + "verbosity": "low", + }, + tool_choice="none", + tools=[ + { + "name": "name", + "parameters": {"foo": "bar"}, + "strict": True, + "type": "function", + "allowed_callers": ["direct"], + "defer_loading": True, + "description": "description", + "output_schema": {"foo": "bar"}, + } + ], + top_logprobs=0, + top_p=1, + truncation="auto", + user="user-1234", + betas=["responses_multi_agent=v1"], + ) + await response_stream.response.aclose() + + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.with_raw_response.create( + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.with_streaming_response.create( + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params_overload_1(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + include=["file_search_call.results"], + include_obfuscation=True, + starting_after=0, + stream=False, + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_raw_response_retrieve_overload_1(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.beta.responses.with_raw_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve_overload_1(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.with_streaming_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_path_params_retrieve_overload_1(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.beta.responses.with_raw_response.retrieve( + response_id="", + ) + + @parametrize + async def test_method_retrieve_overload_2(self, async_client: AsyncOpenAI) -> None: + response_stream = await async_client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + ) + await response_stream.response.aclose() + + @parametrize + async def test_method_retrieve_with_all_params_overload_2(self, async_client: AsyncOpenAI) -> None: + response_stream = await async_client.beta.responses.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + include=["file_search_call.results"], + include_obfuscation=True, + starting_after=0, + betas=["responses_multi_agent=v1"], + ) + await response_stream.response.aclose() + + @parametrize + async def test_raw_response_retrieve_overload_2(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.with_raw_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + ) + + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + stream = response.parse() + await stream.close() + + @parametrize + async def test_streaming_response_retrieve_overload_2(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.with_streaming_response.retrieve( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + stream=True, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + stream = await response.parse() + await stream.close() + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve_overload_2(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.beta.responses.with_raw_response.retrieve( + response_id="", + stream=True, + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert response is None + + @parametrize + async def test_method_delete_with_all_params(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + betas=["responses_multi_agent=v1"], + ) + assert response is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.beta.responses.with_raw_response.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert response is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.with_streaming_response.delete( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert response is None + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.beta.responses.with_raw_response.delete( + response_id="", + ) + + @parametrize + async def test_method_cancel(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_method_cancel_with_all_params(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.beta.responses.with_raw_response.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + @parametrize + async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.with_streaming_response.cancel( + response_id="resp_677efb5139a88190b512bc3fef8e535d", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(BetaResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True + + @parametrize + async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"): + await async_client.beta.responses.with_raw_response.cancel( + response_id="", + ) + + @parametrize + async def test_method_compact(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.compact( + model="gpt-5.6-sol", + ) + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + @parametrize + async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None: + response = await async_client.beta.responses.compact( + model="gpt-5.6-sol", + input="string", + instructions="instructions", + previous_response_id="resp_123", + prompt_cache_key="prompt_cache_key", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, + prompt_cache_retention="in_memory", + service_tier="auto", + betas=["responses_multi_agent=v1"], + ) + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + @parametrize + async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: + http_response = await async_client.beta.responses.with_raw_response.compact( + model="gpt-5.6-sol", + ) + + assert http_response.is_closed is True + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + response = http_response.parse() + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + @parametrize + async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None: + async with async_client.beta.responses.with_streaming_response.compact( + model="gpt-5.6-sol", + ) as http_response: + assert not http_response.is_closed + assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" + + response = await http_response.parse() + assert_matches_type(BetaCompactedResponse, response, path=["response"]) + + assert cast(Any, http_response.is_closed) is True diff --git a/tests/api_resources/beta/test_threads.py b/tests/api_resources/beta/test_threads.py index 7163511951..12f87b69ff 100644 --- a/tests/api_resources/beta/test_threads.py +++ b/tests/api_resources/beta/test_threads.py @@ -248,7 +248,7 @@ def test_method_create_and_run_with_all_params_overload_1(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, response_format="auto", stream=False, @@ -343,7 +343,7 @@ def test_method_create_and_run_with_all_params_overload_2(self, client: OpenAI) max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, response_format="auto", temperature=1, @@ -649,7 +649,7 @@ async def test_method_create_and_run_with_all_params_overload_1(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, response_format="auto", stream=False, @@ -744,7 +744,7 @@ async def test_method_create_and_run_with_all_params_overload_2(self, async_clie max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, response_format="auto", temperature=1, diff --git a/tests/api_resources/beta/threads/test_runs.py b/tests/api_resources/beta/threads/test_runs.py index aa85871325..904c9f79b3 100644 --- a/tests/api_resources/beta/threads/test_runs.py +++ b/tests/api_resources/beta/threads/test_runs.py @@ -57,7 +57,7 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", @@ -148,7 +148,7 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", @@ -607,7 +607,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", @@ -698,7 +698,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_completion_tokens=256, max_prompt_tokens=256, metadata={"foo": "string"}, - model="gpt-5.4", + model="gpt-5.6-sol", parallel_tool_calls=True, reasoning_effort="none", response_format="auto", diff --git a/tests/api_resources/chat/test_completions.py b/tests/api_resources/chat/test_completions.py index 3cba000ae6..6bcf04a74e 100644 --- a/tests/api_resources/chat/test_completions.py +++ b/tests/api_resources/chat/test_completions.py @@ -65,7 +65,13 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tokens=0, metadata={"foo": "string"}, modalities=["text"], - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, n=1, parallel_tool_calls=True, prediction={ @@ -74,6 +80,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, @@ -200,7 +210,13 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tokens=0, metadata={"foo": "string"}, modalities=["text"], - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, n=1, parallel_tool_calls=True, prediction={ @@ -209,6 +225,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, @@ -510,7 +530,13 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_tokens=0, metadata={"foo": "string"}, modalities=["text"], - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, n=1, parallel_tool_calls=True, prediction={ @@ -519,6 +545,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, @@ -645,7 +675,13 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_tokens=0, metadata={"foo": "string"}, modalities=["text"], - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, n=1, parallel_tool_calls=True, prediction={ @@ -654,6 +690,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn }, presence_penalty=-2, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning_effort="none", response_format={"type": "text"}, diff --git a/tests/api_resources/responses/test_input_tokens.py b/tests/api_resources/responses/test_input_tokens.py index 59ff4252b9..b7d15c1e67 100644 --- a/tests/api_resources/responses/test_input_tokens.py +++ b/tests/api_resources/responses/test_input_tokens.py @@ -36,6 +36,7 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None: "context": "auto", "effort": "none", "generate_summary": "auto", + "mode": "standard", "summary": "auto", }, text={ @@ -49,8 +50,10 @@ def test_method_count_with_all_params(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "allowed_callers": ["direct"], "defer_loading": True, "description": "description", + "output_schema": {"foo": "bar"}, } ], truncation="auto", @@ -102,6 +105,7 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> "context": "auto", "effort": "none", "generate_summary": "auto", + "mode": "standard", "summary": "auto", }, text={ @@ -115,8 +119,10 @@ async def test_method_count_with_all_params(self, async_client: AsyncOpenAI) -> "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "allowed_callers": ["direct"], "defer_loading": True, "description": "description", + "output_schema": {"foo": "bar"}, } ], truncation="auto", diff --git a/tests/api_resources/test_responses.py b/tests/api_resources/test_responses.py index 761b410943..9e6f651ba9 100644 --- a/tests/api_resources/test_responses.py +++ b/tests/api_resources/test_responses.py @@ -44,7 +44,13 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -53,11 +59,16 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning={ "context": "auto", "effort": "none", "generate_summary": "auto", + "mode": "standard", "summary": "auto", }, safety_identifier="safety-identifier-1234", @@ -77,8 +88,10 @@ def test_method_create_with_all_params_overload_1(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "allowed_callers": ["direct"], "defer_loading": True, "description": "description", + "output_schema": {"foo": "bar"}, } ], top_logprobs=0, @@ -134,7 +147,13 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -143,11 +162,16 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning={ "context": "auto", "effort": "none", "generate_summary": "auto", + "mode": "standard", "summary": "auto", }, safety_identifier="safety-identifier-1234", @@ -166,8 +190,10 @@ def test_method_create_with_all_params_overload_2(self, client: OpenAI) -> None: "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "allowed_callers": ["direct"], "defer_loading": True, "description": "description", + "output_schema": {"foo": "bar"}, } ], top_logprobs=0, @@ -380,18 +406,22 @@ def test_path_params_cancel(self, client: OpenAI) -> None: @parametrize def test_method_compact(self, client: OpenAI) -> None: response = client.responses.compact( - model="gpt-5.4", + model="gpt-5.6-sol", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize def test_method_compact_with_all_params(self, client: OpenAI) -> None: response = client.responses.compact( - model="gpt-5.4", + model="gpt-5.6-sol", input="string", instructions="instructions", previous_response_id="resp_123", prompt_cache_key="prompt_cache_key", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", service_tier="auto", ) @@ -400,7 +430,7 @@ def test_method_compact_with_all_params(self, client: OpenAI) -> None: @parametrize def test_raw_response_compact(self, client: OpenAI) -> None: http_response = client.responses.with_raw_response.compact( - model="gpt-5.4", + model="gpt-5.6-sol", ) assert http_response.is_closed is True @@ -411,7 +441,7 @@ def test_raw_response_compact(self, client: OpenAI) -> None: @parametrize def test_streaming_response_compact(self, client: OpenAI) -> None: with client.responses.with_streaming_response.compact( - model="gpt-5.4", + model="gpt-5.6-sol", ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -461,7 +491,13 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -470,11 +506,16 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning={ "context": "auto", "effort": "none", "generate_summary": "auto", + "mode": "standard", "summary": "auto", }, safety_identifier="safety-identifier-1234", @@ -494,8 +535,10 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "allowed_callers": ["direct"], "defer_loading": True, "description": "description", + "output_schema": {"foo": "bar"}, } ], top_logprobs=0, @@ -551,7 +594,13 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn max_tool_calls=0, metadata={"foo": "string"}, model="gpt-5.1", - moderation={"model": "model"}, + moderation={ + "model": "model", + "policy": { + "input": {"mode": "score"}, + "output": {"mode": "score"}, + }, + }, parallel_tool_calls=True, previous_response_id="previous_response_id", prompt={ @@ -560,11 +609,16 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "version": "version", }, prompt_cache_key="prompt-cache-key-1234", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", reasoning={ "context": "auto", "effort": "none", "generate_summary": "auto", + "mode": "standard", "summary": "auto", }, safety_identifier="safety-identifier-1234", @@ -583,8 +637,10 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn "parameters": {"foo": "bar"}, "strict": True, "type": "function", + "allowed_callers": ["direct"], "defer_loading": True, "description": "description", + "output_schema": {"foo": "bar"}, } ], top_logprobs=0, @@ -797,18 +853,22 @@ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_method_compact(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.compact( - model="gpt-5.4", + model="gpt-5.6-sol", ) assert_matches_type(CompactedResponse, response, path=["response"]) @parametrize async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) -> None: response = await async_client.responses.compact( - model="gpt-5.4", + model="gpt-5.6-sol", input="string", instructions="instructions", previous_response_id="resp_123", prompt_cache_key="prompt_cache_key", + prompt_cache_options={ + "mode": "implicit", + "ttl": "30m", + }, prompt_cache_retention="in_memory", service_tier="auto", ) @@ -817,7 +877,7 @@ async def test_method_compact_with_all_params(self, async_client: AsyncOpenAI) - @parametrize async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: http_response = await async_client.responses.with_raw_response.compact( - model="gpt-5.4", + model="gpt-5.6-sol", ) assert http_response.is_closed is True @@ -828,7 +888,7 @@ async def test_raw_response_compact(self, async_client: AsyncOpenAI) -> None: @parametrize async def test_streaming_response_compact(self, async_client: AsyncOpenAI) -> None: async with async_client.responses.with_streaming_response.compact( - model="gpt-5.4", + model="gpt-5.6-sol", ) as http_response: assert not http_response.is_closed assert http_response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py index 8e5f16df95..4ed6dff47d 100644 --- a/tests/lib/responses/test_responses.py +++ b/tests/lib/responses/test_responses.py @@ -7,7 +7,11 @@ from inline_snapshot import snapshot from openai import OpenAI, AsyncOpenAI +from openai._types import omit from openai._utils import assert_signatures_in_sync +from openai._models import construct_type_unchecked +from openai.types.responses import Response +from openai.lib._parsing._responses import parse_response from ...conftest import base_url from ..snapshots import make_snapshot_request @@ -29,7 +33,7 @@ def test_output_text(client: OpenAI, respx_mock: MockRouter) -> None: input="What's the weather like in SF?", ), content_snapshot=snapshot( - '{"id": "resp_689a0b2545288193953c892439b42e2800b2e36c65a1fd4b", "object": "response", "created_at": 1754925861, "status": "completed", "background": false, "error": null, "incomplete_details": null, "instructions": null, "max_output_tokens": null, "max_tool_calls": null, "model": "gpt-4o-mini-2024-07-18", "output": [{"id": "msg_689a0b2637b08193ac478e568f49e3f900b2e36c65a1fd4b", "type": "message", "status": "completed", "content": [{"type": "output_text", "annotations": [], "logprobs": [], "text": "I can\'t provide real-time updates, but you can easily check the current weather in San Francisco using a weather website or app. Typically, San Francisco has cool, foggy summers and mild winters, so it\'s good to be prepared for variable weather!"}], "role": "assistant"}], "parallel_tool_calls": true, "previous_response_id": null, "prompt_cache_key": null, "reasoning": {"effort": null, "summary": null}, "safety_identifier": null, "service_tier": "default", "store": true, "temperature": 1.0, "text": {"format": {"type": "text"}, "verbosity": "medium"}, "tool_choice": "auto", "tools": [], "top_logprobs": 0, "top_p": 1.0, "truncation": "disabled", "usage": {"input_tokens": 14, "input_tokens_details": {"cached_tokens": 0}, "output_tokens": 50, "output_tokens_details": {"reasoning_tokens": 0}, "total_tokens": 64}, "user": null, "metadata": {}}' + '{"id": "resp_689a0b2545288193953c892439b42e2800b2e36c65a1fd4b", "object": "response", "created_at": 1754925861, "status": "completed", "background": false, "error": null, "incomplete_details": null, "instructions": null, "max_output_tokens": null, "max_tool_calls": null, "model": "gpt-4o-mini-2024-07-18", "output": [{"id": "msg_689a0b2637b08193ac478e568f49e3f900b2e36c65a1fd4b", "type": "message", "status": "completed", "content": [{"type": "output_text", "annotations": [], "logprobs": [], "text": "I can\'t provide real-time updates, but you can easily check the current weather in San Francisco using a weather website or app. Typically, San Francisco has cool, foggy summers and mild winters, so it\'s good to be prepared for variable weather!"}], "role": "assistant"}], "parallel_tool_calls": true, "previous_response_id": null, "prompt_cache_key": null, "reasoning": {"effort": null, "summary": null}, "safety_identifier": null, "service_tier": "default", "store": true, "temperature": 1.0, "text": {"format": {"type": "text"}, "verbosity": "medium"}, "tool_choice": "auto", "tools": [], "top_logprobs": 0, "top_p": 1.0, "truncation": "disabled", "usage": {"input_tokens": 14, "input_tokens_details": {"cached_tokens": 0, "cache_write_tokens": 0}, "output_tokens": 50, "output_tokens_details": {"reasoning_tokens": 0}, "total_tokens": 64}, "user": null, "metadata": {}}' ), path="/responses", mock_client=client, @@ -41,6 +45,33 @@ def test_output_text(client: OpenAI, respx_mock: MockRouter) -> None: ) +@pytest.mark.parametrize( + "item", + [ + { + "id": "prog_123", + "call_id": "call_123", + "code": "return 42", + "fingerprint": "fp_123", + "type": "program", + }, + { + "id": "prog_out_123", + "call_id": "call_123", + "result": "42", + "status": "completed", + "type": "program_output", + }, + ], +) +def test_parse_response_preserves_program_items(item: dict[str, object]) -> None: + response = construct_type_unchecked(type_=Response, value={"output": [item]}) + + parsed = parse_response(text_format=omit, input_tools=omit, response=response) + + assert parsed.output[0].to_dict() == item + + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: checking_client: OpenAI | AsyncOpenAI = client if sync else async_client From 0e6adb15adc1e74087bcb402de7a75e4fbc0aecb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 22:46:53 -0400 Subject: [PATCH 400/408] release: 2.46.0 (#3501) * codegen metadata * feat(api): add owner_project_access to APIKeyListParams * feat(api): manual updates add service-account api keys doc * feat(api): /organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys" endpoint * fix(api): preserve generated type compatibility * feat(api): manual updates * fix(api): remove beta annotation compatibility aliases * release: 2.46.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: Alex Chang --- .release-please-manifest.json | 2 +- .stats.yml | 8 +- CHANGELOG.md | 17 ++ api.md | 22 +- pyproject.toml | 2 +- src/openai/_version.py | 2 +- .../admin/organization/audit_logs.py | 168 ++++++++++++++ .../admin/organization/projects/api_keys.py | 18 ++ .../admin/organization/projects/projects.py | 16 +- .../projects/service_accounts/__init__.py | 33 +++ .../projects/service_accounts/api_keys.py | 217 ++++++++++++++++++ .../service_accounts.py | 87 +++++-- src/openai/resources/webhooks/api.md | 1 - .../organization/audit_log_list_params.py | 84 +++++++ .../organization/audit_log_list_response.py | 84 +++++++ .../projects/api_key_list_params.py | 11 +- .../organization/projects/project_api_key.py | 3 + .../projects/project_service_account.py | 4 +- .../projects/service_account_create_params.py | 4 + .../service_account_create_response.py | 7 +- .../projects/service_accounts/__init__.py | 6 + .../service_accounts/api_key_create_params.py | 20 ++ .../api_key_create_response.py | 24 ++ .../usage_audio_speeches_response.py | 52 ++++- .../usage_audio_transcriptions_response.py | 52 ++++- ...sage_code_interpreter_sessions_response.py | 52 ++++- .../usage_completions_response.py | 52 ++++- .../organization/usage_costs_response.py | 52 ++++- .../organization/usage_embeddings_response.py | 52 ++++- .../usage_file_search_calls_response.py | 52 ++++- .../organization/usage_images_response.py | 52 ++++- .../usage_moderations_response.py | 52 ++++- .../usage_vector_stores_response.py | 52 ++++- .../usage_web_search_calls_response.py | 52 ++++- .../types/beta/beta_response_input_item.py | 30 ++- .../beta/beta_response_input_item_param.py | 26 ++- .../types/beta/beta_response_input_param.py | 26 ++- .../beta/beta_response_reasoning_item.py | 7 +- .../beta_response_reasoning_item_param.py | 7 +- .../responses/response_reasoning_item.py | 7 +- .../response_reasoning_item_param.py | 7 +- .../types/webhooks/unwrap_webhook_event.py | 2 - .../projects/service_accounts/__init__.py | 1 + .../service_accounts/test_api_keys.py | 140 +++++++++++ .../organization/projects/test_api_keys.py | 2 + .../projects/test_service_accounts.py | 18 ++ 46 files changed, 1494 insertions(+), 191 deletions(-) create mode 100644 src/openai/resources/admin/organization/projects/service_accounts/__init__.py create mode 100644 src/openai/resources/admin/organization/projects/service_accounts/api_keys.py rename src/openai/resources/admin/organization/projects/{ => service_accounts}/service_accounts.py (89%) create mode 100644 src/openai/types/admin/organization/projects/service_accounts/__init__.py create mode 100644 src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py create mode 100644 src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py create mode 100644 tests/api_resources/admin/organization/projects/service_accounts/__init__.py create mode 100644 tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e39a142b9e..7336785395 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.45.0" + ".": "2.46.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 24895ee467..7f13b9ff35 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 271 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-356010b9b9fd6228b457b8fcfa376cf4928a8f3bd4728e7ba5e4b6b5ef4f5843.yml -openapi_spec_hash: 885864ae98a443166f585f856c464fb2 -config_hash: 1f1e3b4050e2cb4bc780ce0b70e2b3e6 +configured_endpoints: 272 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-8ed7dcb0d163e7f5205dbe172d6ad5b7ce367b23c20629830e39a0aee920a029.yml +openapi_spec_hash: 011cdae0c67909fb80a4a5eb54ca5bca +config_hash: 896f0f71bc7d1ee09435813cdef97523 diff --git a/CHANGELOG.md b/CHANGELOG.md index 32b95dda50..5747875ef1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 2.46.0 (2026-07-17) + +Full Changelog: [v2.45.0...v2.46.0](https://github.com/openai/openai-python/compare/v2.45.0...v2.46.0) + +### Features + +* **api:** /organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys" endpoint ([5a00941](https://github.com/openai/openai-python/commit/5a0094194eac9c605c8ca84d47d1c5518f8e2131)) +* **api:** add owner_project_access to APIKeyListParams ([f589d04](https://github.com/openai/openai-python/commit/f589d04bf9f377ecb1f54335ab3ab9d825b5dfee)) +* **api:** manual updates ([980f176](https://github.com/openai/openai-python/commit/980f176e83ee5d991bf9e8e4def80d9905ade5ec)) +* **api:** manual updates ([2eae984](https://github.com/openai/openai-python/commit/2eae984315580cdbf9ceb14d6cb568c581baa768)) + + +### Bug Fixes + +* **api:** preserve generated type compatibility ([00bd72a](https://github.com/openai/openai-python/commit/00bd72adbe03f4b5c4b89d91b8d317f11b58bbdf)) +* **api:** remove beta annotation compatibility aliases ([99dbd15](https://github.com/openai/openai-python/commit/99dbd15ff3ad1628b94a729a6b688212d4655908)) + ## 2.45.0 (2026-07-09) Full Changelog: [v2.44.0...v2.45.0](https://github.com/openai/openai-python/compare/v2.44.0...v2.45.0) diff --git a/api.md b/api.md index 8b44de0b8b..8ee735e5df 100644 --- a/api.md +++ b/api.md @@ -1208,11 +1208,23 @@ from openai.types.admin.organization.projects import ( Methods: -- client.admin.organization.projects.service_accounts.create(project_id, \*\*params) -> ServiceAccountCreateResponse -- client.admin.organization.projects.service_accounts.retrieve(service_account_id, \*, project_id) -> ProjectServiceAccount -- client.admin.organization.projects.service_accounts.update(service_account_id, \*, project_id, \*\*params) -> ProjectServiceAccount -- client.admin.organization.projects.service_accounts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectServiceAccount] -- client.admin.organization.projects.service_accounts.delete(service_account_id, \*, project_id) -> ServiceAccountDeleteResponse +- client.admin.organization.projects.service_accounts.create(project_id, \*\*params) -> ServiceAccountCreateResponse +- client.admin.organization.projects.service_accounts.retrieve(service_account_id, \*, project_id) -> ProjectServiceAccount +- client.admin.organization.projects.service_accounts.update(service_account_id, \*, project_id, \*\*params) -> ProjectServiceAccount +- client.admin.organization.projects.service_accounts.list(project_id, \*\*params) -> SyncConversationCursorPage[ProjectServiceAccount] +- client.admin.organization.projects.service_accounts.delete(service_account_id, \*, project_id) -> ServiceAccountDeleteResponse + +##### APIKeys + +Types: + +```python +from openai.types.admin.organization.projects.service_accounts import APIKeyCreateResponse +``` + +Methods: + +- client.admin.organization.projects.service_accounts.api_keys.create(service_account_id, \*, project_id, \*\*params) -> APIKeyCreateResponse #### APIKeys diff --git a/pyproject.toml b/pyproject.toml index 423289c26e..2ac8884511 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.45.0" +version = "2.46.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index b9f16d5834..a827c30678 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.45.0" # x-release-please-version +__version__ = "2.46.0" # x-release-please-version diff --git a/src/openai/resources/admin/organization/audit_logs.py b/src/openai/resources/admin/organization/audit_logs.py index 1774a80aed..ef4904df62 100644 --- a/src/openai/resources/admin/organization/audit_logs.py +++ b/src/openai/resources/admin/organization/audit_logs.py @@ -112,6 +112,90 @@ def list( "user.added", "user.updated", "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", ] ] | Omit = omit, @@ -293,6 +377,90 @@ def list( "user.added", "user.updated", "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", ] ] | Omit = omit, diff --git a/src/openai/resources/admin/organization/projects/api_keys.py b/src/openai/resources/admin/organization/projects/api_keys.py index 1517d213e0..407bb6b485 100644 --- a/src/openai/resources/admin/organization/projects/api_keys.py +++ b/src/openai/resources/admin/organization/projects/api_keys.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing_extensions import Literal + import httpx from ..... import _legacy_response @@ -89,6 +91,7 @@ def list( *, after: str | Omit = omit, limit: int | Omit = omit, + owner_project_access: Literal["active", "inactive", "any"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -108,6 +111,12 @@ def list( limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. + owner_project_access: Filter API keys by whether the owner currently has effective access to the + project. Use `active` for owners with access, `inactive` for owners without + access, or `any` for all enabled project API keys. If omitted, the endpoint + applies its existing membership-based visibility rules, which may exclude some + enabled keys. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -130,6 +139,7 @@ def list( { "after": after, "limit": limit, + "owner_project_access": owner_project_access, }, api_key_list_params.APIKeyListParams, ), @@ -256,6 +266,7 @@ def list( *, after: str | Omit = omit, limit: int | Omit = omit, + owner_project_access: Literal["active", "inactive", "any"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -275,6 +286,12 @@ def list( limit: A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20. + owner_project_access: Filter API keys by whether the owner currently has effective access to the + project. Use `active` for owners with access, `inactive` for owners without + access, or `any` for all enabled project API keys. If omitted, the endpoint + applies its existing membership-based visibility rules, which may exclude some + enabled keys. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -297,6 +314,7 @@ def list( { "after": after, "limit": limit, + "owner_project_access": owner_project_access, }, api_key_list_params.APIKeyListParams, ), diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py index 6ffd4c0f85..ea2172b594 100644 --- a/src/openai/resources/admin/organization/projects/projects.py +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -78,14 +78,6 @@ AsyncDataRetentionWithStreamingResponse, ) from ....._base_client import AsyncPaginator, make_request_options -from .service_accounts import ( - ServiceAccounts, - AsyncServiceAccounts, - ServiceAccountsWithRawResponse, - AsyncServiceAccountsWithRawResponse, - ServiceAccountsWithStreamingResponse, - AsyncServiceAccountsWithStreamingResponse, -) from .model_permissions import ( ModelPermissions, AsyncModelPermissions, @@ -103,6 +95,14 @@ AsyncHostedToolPermissionsWithStreamingResponse, ) from .....types.admin.organization import project_list_params, project_create_params, project_update_params +from .service_accounts.service_accounts import ( + ServiceAccounts, + AsyncServiceAccounts, + ServiceAccountsWithRawResponse, + AsyncServiceAccountsWithRawResponse, + ServiceAccountsWithStreamingResponse, + AsyncServiceAccountsWithStreamingResponse, +) from .....types.admin.organization.project import Project __all__ = ["Projects", "AsyncProjects"] diff --git a/src/openai/resources/admin/organization/projects/service_accounts/__init__.py b/src/openai/resources/admin/organization/projects/service_accounts/__init__.py new file mode 100644 index 0000000000..598b43ed26 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/service_accounts/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .api_keys import ( + APIKeys, + AsyncAPIKeys, + APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse, +) +from .service_accounts import ( + ServiceAccounts, + AsyncServiceAccounts, + ServiceAccountsWithRawResponse, + AsyncServiceAccountsWithRawResponse, + ServiceAccountsWithStreamingResponse, + AsyncServiceAccountsWithStreamingResponse, +) + +__all__ = [ + "APIKeys", + "AsyncAPIKeys", + "APIKeysWithRawResponse", + "AsyncAPIKeysWithRawResponse", + "APIKeysWithStreamingResponse", + "AsyncAPIKeysWithStreamingResponse", + "ServiceAccounts", + "AsyncServiceAccounts", + "ServiceAccountsWithRawResponse", + "AsyncServiceAccountsWithRawResponse", + "ServiceAccountsWithStreamingResponse", + "AsyncServiceAccountsWithStreamingResponse", +] diff --git a/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py new file mode 100644 index 0000000000..61ef677c53 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/service_accounts/api_keys.py @@ -0,0 +1,217 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...... import _legacy_response +from ......_types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......_base_client import make_request_options +from ......types.admin.organization.projects.service_accounts import api_key_create_params +from ......types.admin.organization.projects.service_accounts.api_key_create_response import APIKeyCreateResponse + +__all__ = ["APIKeys", "AsyncAPIKeys"] + + +class APIKeys(SyncAPIResource): + @cached_property + def with_raw_response(self) -> APIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return APIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> APIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return APIKeysWithStreamingResponse(self) + + def create( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + scopes: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + Creates an API key for a service account in the project. + + Args: + project_id: The ID of the project. + + service_account_id: The ID of the service account. + + name: API key name. + + scopes: API key scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys", + project_id=project_id, + service_account_id=service_account_id, + ), + body=maybe_transform( + { + "name": name, + "scopes": scopes, + }, + api_key_create_params.APIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyCreateResponse, + ) + + +class AsyncAPIKeys(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAPIKeysWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncAPIKeysWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAPIKeysWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncAPIKeysWithStreamingResponse(self) + + async def create( + self, + service_account_id: str, + *, + project_id: str, + name: str | Omit = omit, + scopes: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> APIKeyCreateResponse: + """ + Creates an API key for a service account in the project. + + Args: + project_id: The ID of the project. + + service_account_id: The ID of the service account. + + name: API key name. + + scopes: API key scopes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + if not service_account_id: + raise ValueError(f"Expected a non-empty value for `service_account_id` but received {service_account_id!r}") + return await self._post( + path_template( + "/organization/projects/{project_id}/service_accounts/{service_account_id}/api_keys", + project_id=project_id, + service_account_id=service_account_id, + ), + body=await async_maybe_transform( + { + "name": name, + "scopes": scopes, + }, + api_key_create_params.APIKeyCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=APIKeyCreateResponse, + ) + + +class APIKeysWithRawResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.create = _legacy_response.to_raw_response_wrapper( + api_keys.create, + ) + + +class AsyncAPIKeysWithRawResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.create = _legacy_response.async_to_raw_response_wrapper( + api_keys.create, + ) + + +class APIKeysWithStreamingResponse: + def __init__(self, api_keys: APIKeys) -> None: + self._api_keys = api_keys + + self.create = to_streamed_response_wrapper( + api_keys.create, + ) + + +class AsyncAPIKeysWithStreamingResponse: + def __init__(self, api_keys: AsyncAPIKeys) -> None: + self._api_keys = api_keys + + self.create = async_to_streamed_response_wrapper( + api_keys.create, + ) diff --git a/src/openai/resources/admin/organization/projects/service_accounts.py b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py similarity index 89% rename from src/openai/resources/admin/organization/projects/service_accounts.py rename to src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py index 0de0ced9cf..61dbc8b642 100644 --- a/src/openai/resources/admin/organization/projects/service_accounts.py +++ b/src/openai/resources/admin/organization/projects/service_accounts/service_accounts.py @@ -2,31 +2,44 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Literal import httpx -from ..... import _legacy_response -from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ....._utils import path_template, maybe_transform, async_maybe_transform -from ....._compat import cached_property -from ....._resource import SyncAPIResource, AsyncAPIResource -from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .....pagination import SyncConversationCursorPage, AsyncConversationCursorPage -from ....._base_client import AsyncPaginator, make_request_options -from .....types.admin.organization.projects import ( +from ...... import _legacy_response +from .api_keys import ( + APIKeys, + AsyncAPIKeys, + APIKeysWithRawResponse, + AsyncAPIKeysWithRawResponse, + APIKeysWithStreamingResponse, + AsyncAPIKeysWithStreamingResponse, +) +from ......_types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ......_utils import path_template, maybe_transform, async_maybe_transform +from ......_compat import cached_property +from ......_resource import SyncAPIResource, AsyncAPIResource +from ......_response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ......pagination import SyncConversationCursorPage, AsyncConversationCursorPage +from ......_base_client import AsyncPaginator, make_request_options +from ......types.admin.organization.projects import ( service_account_list_params, service_account_create_params, service_account_update_params, ) -from .....types.admin.organization.projects.project_service_account import ProjectServiceAccount -from .....types.admin.organization.projects.service_account_create_response import ServiceAccountCreateResponse -from .....types.admin.organization.projects.service_account_delete_response import ServiceAccountDeleteResponse +from ......types.admin.organization.projects.project_service_account import ProjectServiceAccount +from ......types.admin.organization.projects.service_account_create_response import ServiceAccountCreateResponse +from ......types.admin.organization.projects.service_account_delete_response import ServiceAccountDeleteResponse __all__ = ["ServiceAccounts", "AsyncServiceAccounts"] class ServiceAccounts(SyncAPIResource): + @cached_property + def api_keys(self) -> APIKeys: + return APIKeys(self._client) + @cached_property def with_raw_response(self) -> ServiceAccountsWithRawResponse: """ @@ -51,6 +64,7 @@ def create( project_id: str, *, name: str, + create_service_account_only: Optional[bool] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -60,12 +74,14 @@ def create( ) -> ServiceAccountCreateResponse: """Creates a new service account in the project. - This also returns an unredacted - API key for the service account. + By default, this also returns an + unredacted API key for the service account. Args: name: The name of the service account being created. + create_service_account_only: Create the service account without default roles or an API key. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -78,7 +94,13 @@ def create( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return self._post( path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), - body=maybe_transform({"name": name}, service_account_create_params.ServiceAccountCreateParams), + body=maybe_transform( + { + "name": name, + "create_service_account_only": create_service_account_only, + }, + service_account_create_params.ServiceAccountCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -294,6 +316,10 @@ def delete( class AsyncServiceAccounts(AsyncAPIResource): + @cached_property + def api_keys(self) -> AsyncAPIKeys: + return AsyncAPIKeys(self._client) + @cached_property def with_raw_response(self) -> AsyncServiceAccountsWithRawResponse: """ @@ -318,6 +344,7 @@ async def create( project_id: str, *, name: str, + create_service_account_only: Optional[bool] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -327,12 +354,14 @@ async def create( ) -> ServiceAccountCreateResponse: """Creates a new service account in the project. - This also returns an unredacted - API key for the service account. + By default, this also returns an + unredacted API key for the service account. Args: name: The name of the service account being created. + create_service_account_only: Create the service account without default roles or an API key. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -345,7 +374,13 @@ async def create( raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") return await self._post( path_template("/organization/projects/{project_id}/service_accounts", project_id=project_id), - body=await async_maybe_transform({"name": name}, service_account_create_params.ServiceAccountCreateParams), + body=await async_maybe_transform( + { + "name": name, + "create_service_account_only": create_service_account_only, + }, + service_account_create_params.ServiceAccountCreateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -580,6 +615,10 @@ def __init__(self, service_accounts: ServiceAccounts) -> None: service_accounts.delete, ) + @cached_property + def api_keys(self) -> APIKeysWithRawResponse: + return APIKeysWithRawResponse(self._service_accounts.api_keys) + class AsyncServiceAccountsWithRawResponse: def __init__(self, service_accounts: AsyncServiceAccounts) -> None: @@ -601,6 +640,10 @@ def __init__(self, service_accounts: AsyncServiceAccounts) -> None: service_accounts.delete, ) + @cached_property + def api_keys(self) -> AsyncAPIKeysWithRawResponse: + return AsyncAPIKeysWithRawResponse(self._service_accounts.api_keys) + class ServiceAccountsWithStreamingResponse: def __init__(self, service_accounts: ServiceAccounts) -> None: @@ -622,6 +665,10 @@ def __init__(self, service_accounts: ServiceAccounts) -> None: service_accounts.delete, ) + @cached_property + def api_keys(self) -> APIKeysWithStreamingResponse: + return APIKeysWithStreamingResponse(self._service_accounts.api_keys) + class AsyncServiceAccountsWithStreamingResponse: def __init__(self, service_accounts: AsyncServiceAccounts) -> None: @@ -642,3 +689,7 @@ def __init__(self, service_accounts: AsyncServiceAccounts) -> None: self.delete = async_to_streamed_response_wrapper( service_accounts.delete, ) + + @cached_property + def api_keys(self) -> AsyncAPIKeysWithStreamingResponse: + return AsyncAPIKeysWithStreamingResponse(self._service_accounts.api_keys) diff --git a/src/openai/resources/webhooks/api.md b/src/openai/resources/webhooks/api.md index 8efcc3e33e..8e3c312eb0 100644 --- a/src/openai/resources/webhooks/api.md +++ b/src/openai/resources/webhooks/api.md @@ -19,7 +19,6 @@ from openai.types.webhooks import ( ResponseCompletedWebhookEvent, ResponseFailedWebhookEvent, ResponseIncompleteWebhookEvent, - SafetyIdentifierBlockedWebhookEvent, UnwrapWebhookEvent, ) ``` diff --git a/src/openai/types/admin/organization/audit_log_list_params.py b/src/openai/types/admin/organization/audit_log_list_params.py index e77e1c8d96..4273a40e21 100644 --- a/src/openai/types/admin/organization/audit_log_list_params.py +++ b/src/openai/types/admin/organization/audit_log_list_params.py @@ -102,6 +102,90 @@ class AuditLogListParams(TypedDict, total=False): "user.added", "user.updated", "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", ] ] """Return only events with a `type` in one of these values. diff --git a/src/openai/types/admin/organization/audit_log_list_response.py b/src/openai/types/admin/organization/audit_log_list_response.py index f2f63c2e74..a19bfe5475 100644 --- a/src/openai/types/admin/organization/audit_log_list_response.py +++ b/src/openai/types/admin/organization/audit_log_list_response.py @@ -1037,6 +1037,90 @@ class AuditLogListResponse(BaseModel): "user.added", "user.updated", "user.deleted", + "tenant.metadata.updated", + "tenant.microsoft_entra_mapping.upserted", + "tenant.microsoft_entra_mapping.deleted", + "tenant.workload_identity.provider.created", + "tenant.workload_identity.provider.updated", + "tenant.workload_identity.provider.archived", + "tenant.workload_identity.mapping.created", + "tenant.workload_identity.mapping.updated", + "tenant.workload_identity.mapping.archived", + "tenant.workload_identity.binding.created", + "tenant.workload_identity.principal.provisioned", + "tenant.admin_api_key.created", + "tenant.admin_api_key.updated", + "tenant.admin_api_key.deleted", + "tenant.project_api_key.created", + "tenant.chatgpt_access_token.revoked", + "tenant.migration.completed", + "tenant.sso.migrated", + "tenant.domains.migrated", + "tenant.sso_connection.created", + "tenant.sso_connection.updated", + "tenant.sso_connection.deleted", + "tenant.sso_connection.setup.started", + "tenant.policy.created", + "tenant.policy.updated", + "tenant.policy.deleted", + "tenant.policy.attached", + "tenant.policy.detached", + "tenant.principal_authentication_policy.resolved", + "tenant.scim.setup.started", + "tenant.scim.deletion.requested", + "tenant.scim.directory.created", + "tenant.product_access_policy.updated", + "tenant.resource_share_grant.created", + "tenant.resource_share_grant.updated", + "tenant.resource_share_grant.accepted", + "tenant.resource_share_grant.declined", + "tenant.resource_share_grant.revoked", + "tenant.resource_share_grant.deleted", + "tenant.service_account.updated", + "tenant.service_account.deleted", + "tenant.service_account.token.revoked", + "tenant.billing.overage_limit.updated", + "tenant.billing.alerts.updated", + "tenant.billing.info.updated", + "tenant.usage_limit.workspace.updated", + "tenant.usage_limit.group.updated", + "tenant.usage_limit.user.updated", + "tenant.usage_limit.increase_request.updated", + "tenant.usage_limit.increase_request.resolved", + "tenant.group.created", + "tenant.group.updated", + "tenant.group.deleted", + "tenant.group.member.added", + "tenant.group.member.removed", + "tenant.migration_rollout.status.updated", + "tenant.migration_rollout.tier.updated", + "tenant.role.metadata.updated", + "tenant.custom_role.created", + "tenant.custom_role.updated", + "tenant.custom_role.deleted", + "tenant.role_assignment.created", + "tenant.role_assignment.deleted", + "tenant.resource_role_assignment.created", + "tenant.resource_role_assignment.deleted", + "tenant.resource_access.updated", + "tenant.resource_access.deleted", + "tenant.session_policy.created", + "tenant.session_policy.updated", + "tenant.session_policy.deleted", + "tenant.session_revocation.started", + "tenant.third_party_app_policy.updated", + "tenant.user.added", + "tenant.user.updated", + "tenant.user.removed", + "tenant.user.looked_up", + "tenant.user.invited", + "tenant.membership.revoked", + "tenant.api_organization_invite.upserted", + "tenant.api_organization_invite.deleted", + "tenant.chatgpt_workspace_invite.upserted", + "tenant.membership.accepted", + "tenant.membership.declined", + "tenant.workspace_invite_email_settings.updated", ] """The event type.""" diff --git a/src/openai/types/admin/organization/projects/api_key_list_params.py b/src/openai/types/admin/organization/projects/api_key_list_params.py index 422a28518e..b83f655a1b 100644 --- a/src/openai/types/admin/organization/projects/api_key_list_params.py +++ b/src/openai/types/admin/organization/projects/api_key_list_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import TypedDict +from typing_extensions import Literal, TypedDict __all__ = ["APIKeyListParams"] @@ -22,3 +22,12 @@ class APIKeyListParams(TypedDict, total=False): Limit can range between 1 and 100, and the default is 20. """ + + owner_project_access: Literal["active", "inactive", "any"] + """ + Filter API keys by whether the owner currently has effective access to the + project. Use `active` for owners with access, `inactive` for owners without + access, or `any` for all enabled project API keys. If omitted, the endpoint + applies its existing membership-based visibility rules, which may exclude some + enabled keys. + """ diff --git a/src/openai/types/admin/organization/projects/project_api_key.py b/src/openai/types/admin/organization/projects/project_api_key.py index 7e5e6949eb..ba08335b39 100644 --- a/src/openai/types/admin/organization/projects/project_api_key.py +++ b/src/openai/types/admin/organization/projects/project_api_key.py @@ -74,5 +74,8 @@ class ProjectAPIKey(BaseModel): owner: Owner + owner_project_access: Literal["active", "inactive"] + """Whether the API key's owner currently has effective access to the project.""" + redacted_value: str """The redacted value of the API key""" diff --git a/src/openai/types/admin/organization/projects/project_service_account.py b/src/openai/types/admin/organization/projects/project_service_account.py index ca7bf0bdae..9082bc72ce 100644 --- a/src/openai/types/admin/organization/projects/project_service_account.py +++ b/src/openai/types/admin/organization/projects/project_service_account.py @@ -22,5 +22,5 @@ class ProjectServiceAccount(BaseModel): object: Literal["organization.project.service_account"] """The object type, which is always `organization.project.service_account`""" - role: Literal["owner", "member"] - """`owner` or `member`""" + role: Literal["owner", "member", "none"] + """`owner`, `member`, or `none`""" diff --git a/src/openai/types/admin/organization/projects/service_account_create_params.py b/src/openai/types/admin/organization/projects/service_account_create_params.py index 409dcba500..0eab9bbb3a 100644 --- a/src/openai/types/admin/organization/projects/service_account_create_params.py +++ b/src/openai/types/admin/organization/projects/service_account_create_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Required, TypedDict __all__ = ["ServiceAccountCreateParams"] @@ -10,3 +11,6 @@ class ServiceAccountCreateParams(TypedDict, total=False): name: Required[str] """The name of the service account being created.""" + + create_service_account_only: Optional[bool] + """Create the service account without default roles or an API key.""" diff --git a/src/openai/types/admin/organization/projects/service_account_create_response.py b/src/openai/types/admin/organization/projects/service_account_create_response.py index 430b11f655..56494e16f9 100644 --- a/src/openai/types/admin/organization/projects/service_account_create_response.py +++ b/src/openai/types/admin/organization/projects/service_account_create_response.py @@ -32,5 +32,8 @@ class ServiceAccountCreateResponse(BaseModel): object: Literal["organization.project.service_account"] - role: Literal["member"] - """Service accounts can only have one role of type `member`""" + role: Literal["member", "none"] + """Service accounts created with default project membership have role `member`. + + Accounts created with `create_service_account_only` have role `none`. + """ diff --git a/src/openai/types/admin/organization/projects/service_accounts/__init__.py b/src/openai/types/admin/organization/projects/service_accounts/__init__.py new file mode 100644 index 0000000000..25cc033b2e --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_accounts/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams +from .api_key_create_response import APIKeyCreateResponse as APIKeyCreateResponse diff --git a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py new file mode 100644 index 0000000000..1f385f110c --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from ......_types import SequenceNotStr + +__all__ = ["APIKeyCreateParams"] + + +class APIKeyCreateParams(TypedDict, total=False): + project_id: Required[str] + """The ID of the project.""" + + name: str + """API key name.""" + + scopes: SequenceNotStr[str] + """API key scopes.""" diff --git a/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py new file mode 100644 index 0000000000..42e8e2f903 --- /dev/null +++ b/src/openai/types/admin/organization/projects/service_accounts/api_key_create_response.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ......_models import BaseModel + +__all__ = ["APIKeyCreateResponse"] + + +class APIKeyCreateResponse(BaseModel): + id: str + """The identifier of the API key.""" + + created_at: int + """The Unix timestamp (in seconds) when the API key was created.""" + + name: str + """The name of the API key.""" + + object: Literal["organization.project.service_account.api_key"] + """The object type, which is always `organization.project.service_account.api_key`""" + + value: str + """The unredacted API key value.""" diff --git a/src/openai/types/admin/organization/usage_audio_speeches_response.py b/src/openai/types/admin/organization/usage_audio_speeches_response.py index f99a73eeb0..186359bf21 100644 --- a/src/openai/types/admin/organization/usage_audio_speeches_response.py +++ b/src/openai/types/admin/organization/usage_audio_speeches_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py index d8f436b0b5..14c440245f 100644 --- a/src/openai/types/admin/organization/usage_audio_transcriptions_response.py +++ b/src/openai/types/admin/organization/usage_audio_transcriptions_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py index 0c92f61446..b8522dde68 100644 --- a/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py +++ b/src/openai/types/admin/organization/usage_code_interpreter_sessions_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_completions_response.py b/src/openai/types/admin/organization/usage_completions_response.py index 57f0b699ec..0a1674d8f1 100644 --- a/src/openai/types/admin/organization/usage_completions_response.py +++ b/src/openai/types/admin/organization/usage_completions_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_costs_response.py b/src/openai/types/admin/organization/usage_costs_response.py index 71eee1188a..348e9db60f 100644 --- a/src/openai/types/admin/organization/usage_costs_response.py +++ b/src/openai/types/admin/organization/usage_costs_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_embeddings_response.py b/src/openai/types/admin/organization/usage_embeddings_response.py index 69697ee4e7..3d8f9d8009 100644 --- a/src/openai/types/admin/organization/usage_embeddings_response.py +++ b/src/openai/types/admin/organization/usage_embeddings_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_file_search_calls_response.py b/src/openai/types/admin/organization/usage_file_search_calls_response.py index 0e093b8346..91f239ac63 100644 --- a/src/openai/types/admin/organization/usage_file_search_calls_response.py +++ b/src/openai/types/admin/organization/usage_file_search_calls_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_images_response.py b/src/openai/types/admin/organization/usage_images_response.py index 33c3ebb130..89738dc73f 100644 --- a/src/openai/types/admin/organization/usage_images_response.py +++ b/src/openai/types/admin/organization/usage_images_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_moderations_response.py b/src/openai/types/admin/organization/usage_moderations_response.py index 3aa5d9193d..413c99cdef 100644 --- a/src/openai/types/admin/organization/usage_moderations_response.py +++ b/src/openai/types/admin/organization/usage_moderations_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_vector_stores_response.py b/src/openai/types/admin/organization/usage_vector_stores_response.py index a5ecc31185..23a9d90ae9 100644 --- a/src/openai/types/admin/organization/usage_vector_stores_response.py +++ b/src/openai/types/admin/organization/usage_vector_stores_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/admin/organization/usage_web_search_calls_response.py b/src/openai/types/admin/organization/usage_web_search_calls_response.py index b6fcd4ac6c..e76fb028fa 100644 --- a/src/openai/types/admin/organization/usage_web_search_calls_response.py +++ b/src/openai/types/admin/organization/usage_web_search_calls_response.py @@ -29,9 +29,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """The aggregated completions usage details of the specific time bucket.""" input_tokens: int - """The aggregated number of text input tokens used, including cached tokens. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of input tokens used, including cached and cache-write + tokens. This includes text, audio, and image tokens. For customers subscribed to + Scale Tier, this includes Scale Tier tokens. """ num_model_requests: int @@ -40,9 +41,10 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): object: Literal["organization.usage.completions.result"] output_tokens: int - """The aggregated number of text output tokens used. - - For customers subscribe to scale tier, this includes scale tier tokens. + """ + The aggregated number of output tokens used across text, audio, and image + outputs. For customers subscribed to Scale Tier, this includes Scale Tier + tokens. """ api_key_id: Optional[str] = None @@ -58,15 +60,41 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): """ input_audio_tokens: Optional[int] = None - """The aggregated number of audio input tokens used, including cached tokens.""" + """The aggregated number of uncached audio input tokens used.""" + + input_cache_write_tokens: Optional[int] = None + """The aggregated number of input tokens written to the cache.""" + + input_cached_audio_tokens: Optional[int] = None + """The aggregated number of cached audio input tokens used.""" + + input_cached_image_tokens: Optional[int] = None + """The aggregated number of cached image input tokens used.""" + + input_cached_text_tokens: Optional[int] = None + """The aggregated number of cached text input tokens used.""" input_cached_tokens: Optional[int] = None """ - The aggregated number of text input tokens that has been cached from previous - requests. For customers subscribe to scale tier, this includes scale tier + The aggregated number of cached input tokens used across text, audio, and image + inputs. For customers subscribed to Scale Tier, this includes Scale Tier tokens. + """ + + input_image_tokens: Optional[int] = None + """The aggregated number of uncached image input tokens used.""" + + input_text_tokens: Optional[int] = None + """ + The aggregated number of uncached text input tokens used, excluding cache-write tokens. """ + input_uncached_tokens: Optional[int] = None + """ + The aggregated number of uncached input tokens used across text, audio, and + image inputs, excluding cache-write tokens. + """ + model: Optional[str] = None """ When `group_by=model`, this field provides the model name of the grouped usage @@ -76,6 +104,12 @@ class DataResultOrganizationUsageCompletionsResult(BaseModel): output_audio_tokens: Optional[int] = None """The aggregated number of audio output tokens used.""" + output_image_tokens: Optional[int] = None + """The aggregated number of image output tokens used.""" + + output_text_tokens: Optional[int] = None + """The aggregated number of text output tokens used.""" + project_id: Optional[str] = None """ When `group_by=project_id`, this field provides the project ID of the grouped diff --git a/src/openai/types/beta/beta_response_input_item.py b/src/openai/types/beta/beta_response_input_item.py index d493bd040a..bb26d58f6e 100644 --- a/src/openai/types/beta/beta_response_input_item.py +++ b/src/openai/types/beta/beta_response_input_item.py @@ -47,9 +47,10 @@ "MultiAgentCallAgent", "MultiAgentCallOutput", "MultiAgentCallOutputOutput", - "MultiAgentCallOutputOutputAnnotationsUnionMember0", - "MultiAgentCallOutputOutputAnnotationsUnionMember1", - "MultiAgentCallOutputOutputAnnotationsUnionMember2", + "MultiAgentCallOutputOutputAnnotation", + "MultiAgentCallOutputOutputAnnotationFileCitation", + "MultiAgentCallOutputOutputAnnotationURLCitation", + "MultiAgentCallOutputOutputAnnotationContainerFileCitation", "MultiAgentCallOutputAgent", "ToolSearchCall", "ToolSearchCallAgent", @@ -328,7 +329,7 @@ class MultiAgentCall(BaseModel): """The agent that produced this item.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember0(BaseModel): +class MultiAgentCallOutputOutputAnnotationFileCitation(BaseModel): file_id: str """The ID of the file.""" @@ -342,7 +343,7 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember0(BaseModel): """The citation type. Always `file_citation`.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember1(BaseModel): +class MultiAgentCallOutputOutputAnnotationURLCitation(BaseModel): end_index: int """The index of the last character of the citation in the message.""" @@ -359,7 +360,7 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember1(BaseModel): """The URL of the cited resource.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember2(BaseModel): +class MultiAgentCallOutputOutputAnnotationContainerFileCitation(BaseModel): container_id: str """The ID of the container.""" @@ -379,6 +380,16 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember2(BaseModel): """The citation type. Always `container_file_citation`.""" +MultiAgentCallOutputOutputAnnotation: TypeAlias = Annotated[ + Union[ + MultiAgentCallOutputOutputAnnotationFileCitation, + MultiAgentCallOutputOutputAnnotationURLCitation, + MultiAgentCallOutputOutputAnnotationContainerFileCitation, + ], + PropertyInfo(discriminator="type"), +] + + class MultiAgentCallOutputOutput(BaseModel): text: str """The text content.""" @@ -386,12 +397,7 @@ class MultiAgentCallOutputOutput(BaseModel): type: Literal["output_text"] """The content type. Always `output_text`.""" - annotations: Union[ - List[MultiAgentCallOutputOutputAnnotationsUnionMember0], - List[MultiAgentCallOutputOutputAnnotationsUnionMember1], - List[MultiAgentCallOutputOutputAnnotationsUnionMember2], - None, - ] = None + annotations: Optional[List[MultiAgentCallOutputOutputAnnotation]] = None """Citations associated with the text content.""" diff --git a/src/openai/types/beta/beta_response_input_item_param.py b/src/openai/types/beta/beta_response_input_item_param.py index c41c57d860..d121d8295f 100644 --- a/src/openai/types/beta/beta_response_input_item_param.py +++ b/src/openai/types/beta/beta_response_input_item_param.py @@ -48,9 +48,10 @@ "MultiAgentCallAgent", "MultiAgentCallOutput", "MultiAgentCallOutputOutput", - "MultiAgentCallOutputOutputAnnotationsUnionMember0", - "MultiAgentCallOutputOutputAnnotationsUnionMember1", - "MultiAgentCallOutputOutputAnnotationsUnionMember2", + "MultiAgentCallOutputOutputAnnotation", + "MultiAgentCallOutputOutputAnnotationFileCitation", + "MultiAgentCallOutputOutputAnnotationURLCitation", + "MultiAgentCallOutputOutputAnnotationContainerFileCitation", "MultiAgentCallOutputAgent", "ToolSearchCall", "ToolSearchCallAgent", @@ -328,7 +329,7 @@ class MultiAgentCall(TypedDict, total=False): """The agent that produced this item.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember0(TypedDict, total=False): +class MultiAgentCallOutputOutputAnnotationFileCitation(TypedDict, total=False): file_id: Required[str] """The ID of the file.""" @@ -342,7 +343,7 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember0(TypedDict, total=False): """The citation type. Always `file_citation`.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember1(TypedDict, total=False): +class MultiAgentCallOutputOutputAnnotationURLCitation(TypedDict, total=False): end_index: Required[int] """The index of the last character of the citation in the message.""" @@ -359,7 +360,7 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember1(TypedDict, total=False): """The URL of the cited resource.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember2(TypedDict, total=False): +class MultiAgentCallOutputOutputAnnotationContainerFileCitation(TypedDict, total=False): container_id: Required[str] """The ID of the container.""" @@ -379,6 +380,13 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember2(TypedDict, total=False): """The citation type. Always `container_file_citation`.""" +MultiAgentCallOutputOutputAnnotation: TypeAlias = Union[ + MultiAgentCallOutputOutputAnnotationFileCitation, + MultiAgentCallOutputOutputAnnotationURLCitation, + MultiAgentCallOutputOutputAnnotationContainerFileCitation, +] + + class MultiAgentCallOutputOutput(TypedDict, total=False): text: Required[str] """The text content.""" @@ -386,11 +394,7 @@ class MultiAgentCallOutputOutput(TypedDict, total=False): type: Required[Literal["output_text"]] """The content type. Always `output_text`.""" - annotations: Union[ - Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember0], - Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember1], - Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember2], - ] + annotations: Iterable[MultiAgentCallOutputOutputAnnotation] """Citations associated with the text content.""" diff --git a/src/openai/types/beta/beta_response_input_param.py b/src/openai/types/beta/beta_response_input_param.py index 4b8ed10f54..bdf4b36465 100644 --- a/src/openai/types/beta/beta_response_input_param.py +++ b/src/openai/types/beta/beta_response_input_param.py @@ -49,9 +49,10 @@ "MultiAgentCallAgent", "MultiAgentCallOutput", "MultiAgentCallOutputOutput", - "MultiAgentCallOutputOutputAnnotationsUnionMember0", - "MultiAgentCallOutputOutputAnnotationsUnionMember1", - "MultiAgentCallOutputOutputAnnotationsUnionMember2", + "MultiAgentCallOutputOutputAnnotation", + "MultiAgentCallOutputOutputAnnotationFileCitation", + "MultiAgentCallOutputOutputAnnotationURLCitation", + "MultiAgentCallOutputOutputAnnotationContainerFileCitation", "MultiAgentCallOutputAgent", "ToolSearchCall", "ToolSearchCallAgent", @@ -329,7 +330,7 @@ class MultiAgentCall(TypedDict, total=False): """The agent that produced this item.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember0(TypedDict, total=False): +class MultiAgentCallOutputOutputAnnotationFileCitation(TypedDict, total=False): file_id: Required[str] """The ID of the file.""" @@ -343,7 +344,7 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember0(TypedDict, total=False): """The citation type. Always `file_citation`.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember1(TypedDict, total=False): +class MultiAgentCallOutputOutputAnnotationURLCitation(TypedDict, total=False): end_index: Required[int] """The index of the last character of the citation in the message.""" @@ -360,7 +361,7 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember1(TypedDict, total=False): """The URL of the cited resource.""" -class MultiAgentCallOutputOutputAnnotationsUnionMember2(TypedDict, total=False): +class MultiAgentCallOutputOutputAnnotationContainerFileCitation(TypedDict, total=False): container_id: Required[str] """The ID of the container.""" @@ -380,6 +381,13 @@ class MultiAgentCallOutputOutputAnnotationsUnionMember2(TypedDict, total=False): """The citation type. Always `container_file_citation`.""" +MultiAgentCallOutputOutputAnnotation: TypeAlias = Union[ + MultiAgentCallOutputOutputAnnotationFileCitation, + MultiAgentCallOutputOutputAnnotationURLCitation, + MultiAgentCallOutputOutputAnnotationContainerFileCitation, +] + + class MultiAgentCallOutputOutput(TypedDict, total=False): text: Required[str] """The text content.""" @@ -387,11 +395,7 @@ class MultiAgentCallOutputOutput(TypedDict, total=False): type: Required[Literal["output_text"]] """The content type. Always `output_text`.""" - annotations: Union[ - Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember0], - Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember1], - Iterable[MultiAgentCallOutputOutputAnnotationsUnionMember2], - ] + annotations: Iterable[MultiAgentCallOutputOutputAnnotation] """Citations associated with the text content.""" diff --git a/src/openai/types/beta/beta_response_reasoning_item.py b/src/openai/types/beta/beta_response_reasoning_item.py index 57bed79f63..14d3c5abe7 100644 --- a/src/openai/types/beta/beta_response_reasoning_item.py +++ b/src/openai/types/beta/beta_response_reasoning_item.py @@ -59,9 +59,10 @@ class BetaResponseReasoningItem(BaseModel): """Reasoning text content.""" encrypted_content: Optional[str] = None - """ - The encrypted content of the reasoning item - populated when a response is - generated with `reasoning.encrypted_content` in the `include` parameter. + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. """ status: Optional[Literal["in_progress", "completed", "incomplete"]] = None diff --git a/src/openai/types/beta/beta_response_reasoning_item_param.py b/src/openai/types/beta/beta_response_reasoning_item_param.py index 8a91128af8..608a31189f 100644 --- a/src/openai/types/beta/beta_response_reasoning_item_param.py +++ b/src/openai/types/beta/beta_response_reasoning_item_param.py @@ -59,9 +59,10 @@ class BetaResponseReasoningItemParam(TypedDict, total=False): """Reasoning text content.""" encrypted_content: Optional[str] - """ - The encrypted content of the reasoning item - populated when a response is - generated with `reasoning.encrypted_content` in the `include` parameter. + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. """ status: Literal["in_progress", "completed", "incomplete"] diff --git a/src/openai/types/responses/response_reasoning_item.py b/src/openai/types/responses/response_reasoning_item.py index 1a22eb60cc..7f24ef2c57 100644 --- a/src/openai/types/responses/response_reasoning_item.py +++ b/src/openai/types/responses/response_reasoning_item.py @@ -49,9 +49,10 @@ class ResponseReasoningItem(BaseModel): """Reasoning text content.""" encrypted_content: Optional[str] = None - """ - The encrypted content of the reasoning item - populated when a response is - generated with `reasoning.encrypted_content` in the `include` parameter. + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. """ status: Optional[Literal["in_progress", "completed", "incomplete"]] = None diff --git a/src/openai/types/responses/response_reasoning_item_param.py b/src/openai/types/responses/response_reasoning_item_param.py index 40320b72e1..e4cbc884e3 100644 --- a/src/openai/types/responses/response_reasoning_item_param.py +++ b/src/openai/types/responses/response_reasoning_item_param.py @@ -49,9 +49,10 @@ class ResponseReasoningItemParam(TypedDict, total=False): """Reasoning text content.""" encrypted_content: Optional[str] - """ - The encrypted content of the reasoning item - populated when a response is - generated with `reasoning.encrypted_content` in the `include` parameter. + """The encrypted content of the reasoning item. + + This is populated by default for reasoning items returned by + `POST /v1/responses` and WebSocket `response.create` requests. """ status: Literal["in_progress", "completed", "incomplete"] diff --git a/src/openai/types/webhooks/unwrap_webhook_event.py b/src/openai/types/webhooks/unwrap_webhook_event.py index 20b77c493f..952383c049 100644 --- a/src/openai/types/webhooks/unwrap_webhook_event.py +++ b/src/openai/types/webhooks/unwrap_webhook_event.py @@ -19,7 +19,6 @@ from .realtime_call_incoming_webhook_event import RealtimeCallIncomingWebhookEvent from .fine_tuning_job_cancelled_webhook_event import FineTuningJobCancelledWebhookEvent from .fine_tuning_job_succeeded_webhook_event import FineTuningJobSucceededWebhookEvent -from .safety_identifier_blocked_webhook_event import SafetyIdentifierBlockedWebhookEvent __all__ = ["UnwrapWebhookEvent"] @@ -40,7 +39,6 @@ ResponseCompletedWebhookEvent, ResponseFailedWebhookEvent, ResponseIncompleteWebhookEvent, - SafetyIdentifierBlockedWebhookEvent, ], PropertyInfo(discriminator="type"), ] diff --git a/tests/api_resources/admin/organization/projects/service_accounts/__init__.py b/tests/api_resources/admin/organization/projects/service_accounts/__init__.py new file mode 100644 index 0000000000..fd8019a9a1 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/service_accounts/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py b/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py new file mode 100644 index 0000000000..e3517f0053 --- /dev/null +++ b/tests/api_resources/admin/organization/projects/service_accounts/test_api_keys.py @@ -0,0 +1,140 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects.service_accounts import APIKeyCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAPIKeys: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: OpenAI) -> None: + api_key = client.admin.organization.projects.service_accounts.api_keys.create( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + api_key = client.admin.organization.projects.service_accounts.api_keys.create( + service_account_id="service_account_id", + project_id="project_id", + name="name", + scopes=["string"], + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: OpenAI) -> None: + response = client.admin.organization.projects.service_accounts.api_keys.with_raw_response.create( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: OpenAI) -> None: + with client.admin.organization.projects.service_accounts.api_keys.with_streaming_response.create( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_create(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.service_accounts.api_keys.with_raw_response.create( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + client.admin.organization.projects.service_accounts.api_keys.with_raw_response.create( + service_account_id="", + project_id="project_id", + ) + + +class TestAsyncAPIKeys: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncOpenAI) -> None: + api_key = await async_client.admin.organization.projects.service_accounts.api_keys.create( + service_account_id="service_account_id", + project_id="project_id", + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + api_key = await async_client.admin.organization.projects.service_accounts.api_keys.create( + service_account_id="service_account_id", + project_id="project_id", + name="name", + scopes=["string"], + ) + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.service_accounts.api_keys.with_raw_response.create( + service_account_id="service_account_id", + project_id="project_id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + api_key = response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.service_accounts.api_keys.with_streaming_response.create( + service_account_id="service_account_id", + project_id="project_id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_key = await response.parse() + assert_matches_type(APIKeyCreateResponse, api_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_create(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.api_keys.with_raw_response.create( + service_account_id="service_account_id", + project_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `service_account_id` but received ''"): + await async_client.admin.organization.projects.service_accounts.api_keys.with_raw_response.create( + service_account_id="", + project_id="project_id", + ) diff --git a/tests/api_resources/admin/organization/projects/test_api_keys.py b/tests/api_resources/admin/organization/projects/test_api_keys.py index f9aef44216..b2717c59d2 100644 --- a/tests/api_resources/admin/organization/projects/test_api_keys.py +++ b/tests/api_resources/admin/organization/projects/test_api_keys.py @@ -79,6 +79,7 @@ def test_method_list_with_all_params(self, client: OpenAI) -> None: project_id="project_id", after="after", limit=0, + owner_project_access="active", ) assert_matches_type(SyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) @@ -228,6 +229,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenAI) -> N project_id="project_id", after="after", limit=0, + owner_project_access="active", ) assert_matches_type(AsyncConversationCursorPage[ProjectAPIKey], api_key, path=["response"]) diff --git a/tests/api_resources/admin/organization/projects/test_service_accounts.py b/tests/api_resources/admin/organization/projects/test_service_accounts.py index e8e68596a2..5b0daafb16 100644 --- a/tests/api_resources/admin/organization/projects/test_service_accounts.py +++ b/tests/api_resources/admin/organization/projects/test_service_accounts.py @@ -30,6 +30,15 @@ def test_method_create(self, client: OpenAI) -> None: ) assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + @parametrize + def test_method_create_with_all_params(self, client: OpenAI) -> None: + service_account = client.admin.organization.projects.service_accounts.create( + project_id="project_id", + name="name", + create_service_account_only=True, + ) + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + @parametrize def test_raw_response_create(self, client: OpenAI) -> None: response = client.admin.organization.projects.service_accounts.with_raw_response.create( @@ -279,6 +288,15 @@ async def test_method_create(self, async_client: AsyncOpenAI) -> None: ) assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None: + service_account = await async_client.admin.organization.projects.service_accounts.create( + project_id="project_id", + name="name", + create_service_account_only=True, + ) + assert_matches_type(ServiceAccountCreateResponse, service_account, path=["response"]) + @parametrize async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None: response = await async_client.admin.organization.projects.service_accounts.with_raw_response.create( From b140f45c2c1850306a91113245639a0a41f19f28 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Fri, 17 Jul 2026 12:13:34 -0700 Subject: [PATCH 401/408] ci: Set up CodeQL scanning (#3514) --- .github/workflows/codeql.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..f59c0560ba --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,32 @@ +name: CodeQL + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze Python + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4 + with: + languages: python + + - name: Analyze + uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4 From d4dceb221b9a92c55c232d5b330ae89beb539415 Mon Sep 17 00:00:00 2001 From: Justin Beckwith Date: Fri, 17 Jul 2026 13:28:12 -0700 Subject: [PATCH 402/408] fix(deps): require patched aiohttp on Python 3.10+ (#3515) * fix(deps): require patched aiohttp on Python 3.10+ * fix: keep aiohttp optional on Python 3.9 --- README.md | 2 + pyproject.toml | 5 +- requirements-dev.lock | 27 -- requirements.lock | 28 -- src/openai/_base_client.py | 29 +- tests/conftest.py | 4 + uv.lock | 628 ++++++++----------------------------- 7 files changed, 151 insertions(+), 572 deletions(-) diff --git a/README.md b/README.md index df46c6b690..dd6c9d968d 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,8 @@ Functionality between the synchronous and asynchronous clients is otherwise iden By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. +The `aiohttp` backend requires Python 3.10 or later. + You can enable this by installing `aiohttp`: ```sh diff --git a/pyproject.toml b/pyproject.toml index 2ac8884511..0256365772 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,10 @@ Homepage = "https://github.com/openai/openai-python" Repository = "https://github.com/openai/openai-python" [project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] +aiohttp = [ + "aiohttp>=3.14.1; python_version >= '3.10'", + "httpx_aiohttp>=0.1.9; python_version >= '3.10'", +] realtime = ["websockets >= 13, < 16"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] diff --git a/requirements-dev.lock b/requirements-dev.lock index 9ddb744eaf..eb40146634 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,13 +10,6 @@ # universal: false -e file:. -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.13.3 - # via httpx-aiohttp - # via openai -aiosignal==1.4.0 - # via aiohttp annotated-types==0.7.0 # via pydantic anyio==4.12.1 @@ -26,10 +19,7 @@ argcomplete==3.6.3 # via nox asttokens==3.0.1 # via inline-snapshot -async-timeout==5.0.1 - # via aiohttp attrs==25.4.0 - # via aiohttp # via jsonschema # via nox # via outcome @@ -76,20 +66,14 @@ executing==2.2.1 # via inline-snapshot filelock==3.19.1 # via virtualenv -frozenlist==1.8.0 - # via aiohttp - # via aiosignal griffe==1.14.0 h11==0.16.0 # via httpcore httpcore==1.0.9 # via httpx httpx==0.28.1 - # via httpx-aiohttp # via openai # via respx -httpx-aiohttp==0.1.12 - # via openai humanize==4.13.0 # via nox idna==3.11 @@ -97,7 +81,6 @@ idna==3.11 # via httpx # via requests # via trio - # via yarl importlib-metadata==8.7.1 iniconfig==2.1.0 # via pytest @@ -118,9 +101,6 @@ msal==1.34.0 # via msal-extensions msal-extensions==1.3.1 # via azure-identity -multidict==6.7.0 - # via aiohttp - # via yarl mypy==1.17.0 mypy-extensions==1.1.0 # via mypy @@ -148,9 +128,6 @@ platformdirs==4.4.0 # via virtualenv pluggy==1.6.0 # via pytest -propcache==0.4.1 - # via aiohttp - # via yarl pycparser==2.23 # via cffi pydantic==2.12.5 @@ -216,13 +193,11 @@ types-tqdm==4.67.0.20250809 types-urllib3==1.26.25.14 # via types-requests typing-extensions==4.15.0 - # via aiosignal # via anyio # via azure-core # via azure-identity # via cryptography # via exceptiongroup - # via multidict # via mypy # via openai # via pydantic @@ -243,7 +218,5 @@ virtualenv==20.35.4 # via nox websockets==15.0.1 # via openai -yarl==1.22.0 - # via aiohttp zipp==3.23.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 4a209f1034..18219dd7f1 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,22 +10,11 @@ # universal: false -e file:. -aiohappyeyeballs==2.6.1 - # via aiohttp -aiohttp==3.13.3 - # via httpx-aiohttp - # via openai -aiosignal==1.4.0 - # via aiohttp annotated-types==0.7.0 # via pydantic anyio==4.12.1 # via httpx # via openai -async-timeout==5.0.1 - # via aiohttp -attrs==25.4.0 - # via aiohttp botocore==1.42.97 # via openai certifi==2026.1.4 @@ -37,29 +26,19 @@ distro==1.9.0 # via openai exceptiongroup==1.3.1 # via anyio -frozenlist==1.8.0 - # via aiohttp - # via aiosignal h11==0.16.0 # via httpcore httpcore==1.0.9 # via httpx httpx==0.28.1 - # via httpx-aiohttp - # via openai -httpx-aiohttp==0.1.12 # via openai idna==3.11 # via anyio # via httpx - # via yarl jiter==0.12.0 # via openai jmespath==1.1.0 # via botocore -multidict==6.7.0 - # via aiohttp - # via yarl numpy==2.0.2 # via openai # via pandas @@ -68,9 +47,6 @@ pandas==2.3.3 # via openai pandas-stubs==2.2.2.240807 # via openai -propcache==0.4.1 - # via aiohttp - # via yarl pycparser==2.23 # via cffi pydantic==2.12.5 @@ -93,10 +69,8 @@ tqdm==4.67.1 types-pytz==2025.2.0.20251108 # via pandas-stubs typing-extensions==4.15.0 - # via aiosignal # via anyio # via exceptiongroup - # via multidict # via openai # via pydantic # via pydantic-core @@ -109,5 +83,3 @@ urllib3==1.26.20 # via botocore websockets==15.0.1 # via openai -yarl==1.22.0 - # via aiohttp diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 216b36aabd..4933c8e2fe 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -1426,22 +1426,29 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) -try: - import httpx_aiohttp -except ImportError: - +if sys.version_info < (3, 10): class _DefaultAioHttpClient(httpx.AsyncClient): def __init__(self, **_kwargs: Any) -> None: - raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") + raise RuntimeError("The aiohttp client requires Python 3.10 or later") else: + try: + import httpx_aiohttp + except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError( + "To use the aiohttp client you must have installed the package with the `aiohttp` extra" + ) + else: - class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore - def __init__(self, **kwargs: Any) -> None: - kwargs.setdefault("timeout", DEFAULT_TIMEOUT) - kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) - kwargs.setdefault("follow_redirects", True) + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) - super().__init__(**kwargs) + super().__init__(**kwargs) if TYPE_CHECKING: diff --git a/tests/conftest.py b/tests/conftest.py index 1042fe59d9..74aebd8a25 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +import sys import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator @@ -77,6 +78,9 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenAI]: http_client_type = param.get("http_client", "httpx") if http_client_type == "aiohttp": + if sys.version_info < (3, 10): + pytest.skip("the aiohttp client requires Python 3.10 or later") + http_client = DefaultAioHttpClient() else: raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") diff --git a/uv.lock b/uv.lock index 3677540704..d3c4fe3ba4 100644 --- a/uv.lock +++ b/uv.lock @@ -12,199 +12,29 @@ resolution-markers = [ "python_full_version < '3.10'", ] -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - [[package]] name = "aiohappyeyeballs" version = "2.6.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, ] -[[package]] -name = "aiohttp" -version = "3.13.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "aiohappyeyeballs", version = "2.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "aiosignal", marker = "python_full_version < '3.10'" }, - { name = "async-timeout", marker = "python_full_version < '3.10'" }, - { name = "attrs", marker = "python_full_version < '3.10'" }, - { name = "frozenlist", marker = "python_full_version < '3.10'" }, - { name = "multidict", marker = "python_full_version < '3.10'" }, - { name = "propcache", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "yarl", version = "1.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/85/cebc47ee74d8b408749073a1a46c6fcba13d170dc8af7e61996c6c9394ac/aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b", size = 750547, upload-time = "2026-03-31T21:56:30.024Z" }, - { url = "https://files.pythonhosted.org/packages/05/98/afd308e35b9d3d8c9ec54c0918f1d722c86dc17ddfec272fcdbcce5a3124/aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5", size = 503535, upload-time = "2026-03-31T21:56:31.935Z" }, - { url = "https://files.pythonhosted.org/packages/6f/4d/926c183e06b09d5270a309eb50fbde7b09782bfd305dec1e800f329834fb/aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670", size = 497830, upload-time = "2026-03-31T21:56:33.654Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d6/f47d1c690f115a5c2a5e8938cce4a232a5be9aac5c5fb2647efcbbbda333/aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274", size = 1682474, upload-time = "2026-03-31T21:56:35.513Z" }, - { url = "https://files.pythonhosted.org/packages/01/44/056fd37b1bb52eac760303e5196acc74d9d546631b035704ae5927f7b4ac/aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a", size = 1655259, upload-time = "2026-03-31T21:56:37.843Z" }, - { url = "https://files.pythonhosted.org/packages/91/9f/78eb1a20c1c28ae02f6a3c0f4d7b0dcc66abce5290cadd53d78ce3084175/aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d", size = 1736204, upload-time = "2026-03-31T21:56:39.822Z" }, - { url = "https://files.pythonhosted.org/packages/de/6c/d20d7de23f0b52b8c1d9e2033b2db1ac4dacbb470bb74c56de0f5f86bb4f/aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796", size = 1826198, upload-time = "2026-03-31T21:56:41.378Z" }, - { url = "https://files.pythonhosted.org/packages/2f/86/a6f3ff1fd795f49545a7c74b2c92f62729135d73e7e4055bf74da5a26c82/aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95", size = 1681329, upload-time = "2026-03-31T21:56:43.374Z" }, - { url = "https://files.pythonhosted.org/packages/fb/68/84cd3dab6b7b4f3e6fe9459a961acb142aaab846417f6e8905110d7027e5/aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5", size = 1560023, upload-time = "2026-03-31T21:56:45.031Z" }, - { url = "https://files.pythonhosted.org/packages/41/2c/db61b64b0249e30f954a65ab4cb4970ced57544b1de2e3c98ee5dc24165f/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a", size = 1652372, upload-time = "2026-03-31T21:56:47.075Z" }, - { url = "https://files.pythonhosted.org/packages/25/6f/e96988a6c982d047810c772e28c43c64c300c943b0ed5c1c0c4ce1e1027c/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73", size = 1662031, upload-time = "2026-03-31T21:56:48.835Z" }, - { url = "https://files.pythonhosted.org/packages/b7/26/a56feace81f3d347b4052403a9d03754a0ab23f7940780dada0849a38c92/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297", size = 1708118, upload-time = "2026-03-31T21:56:50.833Z" }, - { url = "https://files.pythonhosted.org/packages/78/6e/b6173a8ff03d01d5e1a694bc06764b5dad1df2d4ed8f0ceec12bb3277936/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074", size = 1548667, upload-time = "2026-03-31T21:56:52.81Z" }, - { url = "https://files.pythonhosted.org/packages/16/13/13296ffe2c132d888b3fe2c195c8b9c0c24c89c3fa5cc2c44464dc23b22e/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e", size = 1724490, upload-time = "2026-03-31T21:56:54.541Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b4/1f1c287f4a79782ef36e5a6e62954c85343bc30470d862d30bd5f26c9fa2/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7", size = 1667109, upload-time = "2026-03-31T21:56:56.21Z" }, - { url = "https://files.pythonhosted.org/packages/ef/42/8461a2aaf60a8f4ea4549a4056be36b904b0eb03d97ca9a8a2604681a500/aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9", size = 439478, upload-time = "2026-03-31T21:56:58.292Z" }, - { url = "https://files.pythonhosted.org/packages/e5/71/06956304cb5ee439dfe8d86e1b2e70088bd88ed1ced1f42fb29e5d855f0e/aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76", size = 462047, upload-time = "2026-03-31T21:57:00.257Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, - { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, - { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, - { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, - { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, - { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, - { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, - { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, - { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, - { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, - { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, - { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, - { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, - { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, - { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, - { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, - { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, - { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, - { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, - { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, - { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, - { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, - { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, - { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, - { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, - { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, - { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, - { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, - { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, - { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, - { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, - { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, - { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, - { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, - { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, - { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, - { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, - { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, - { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, - { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, - { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, - { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, - { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, - { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, - { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, - { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, - { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, - { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, - { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, - { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, - { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, - { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, - { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, - { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, - { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, - { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, - { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, - { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, - { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a5/630bc484695d4a1342bbae85fb8689bf979106525684fc88f05b397324ad/aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf", size = 752872, upload-time = "2026-03-31T22:00:15.553Z" }, - { url = "https://files.pythonhosted.org/packages/cd/b8/6a19dda37fda94a9ebefb3c1ae0ff419ac7fbf4fb40750e992829fc13614/aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1", size = 504582, upload-time = "2026-03-31T22:00:18.191Z" }, - { url = "https://files.pythonhosted.org/packages/d5/34/8413eafee3421ade2d6ce9e7c0da1213e1d7f0049be09dcdc342b03a39ba/aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10", size = 499094, upload-time = "2026-03-31T22:00:21.118Z" }, - { url = "https://files.pythonhosted.org/packages/da/cf/c6f97006093d1e8ca40fbab843ff49ec7725ab668f0714dd1cb702c62cbd/aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f", size = 1669505, upload-time = "2026-03-31T22:00:24.01Z" }, - { url = "https://files.pythonhosted.org/packages/c2/27/3b2288e66dcec8b04771b2bee3909f70e4072bea995cde5ab7e775e73ddc/aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b", size = 1648928, upload-time = "2026-03-31T22:00:27.001Z" }, - { url = "https://files.pythonhosted.org/packages/3a/7f/605d766887594a88dcc27a19663499c7c5e13e7aa87f129b763765a2ee63/aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643", size = 1731800, upload-time = "2026-03-31T22:00:29.603Z" }, - { url = "https://files.pythonhosted.org/packages/71/94/5a878e728e30699d22b118f1a6ad576ab6fff9eb2c6fc8a7faa9376a1c3e/aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031", size = 1824247, upload-time = "2026-03-31T22:00:32.139Z" }, - { url = "https://files.pythonhosted.org/packages/37/99/84b448291e9996bb83bf4fad3a71a9786d542f19c50a3ff0531bfaba6fac/aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258", size = 1670742, upload-time = "2026-03-31T22:00:34.788Z" }, - { url = "https://files.pythonhosted.org/packages/14/a8/d8d5d1ab6d29a4a3bdb9db31f161e338bfdf6638f6574ea8380f1d4a243c/aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a", size = 1562474, upload-time = "2026-03-31T22:00:37.623Z" }, - { url = "https://files.pythonhosted.org/packages/92/e8/bd889697916f10b65524422c61b4eeaf919eb35a170290cccb680cbe4eb4/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88", size = 1642235, upload-time = "2026-03-31T22:00:40.541Z" }, - { url = "https://files.pythonhosted.org/packages/60/42/3f1928107131f1413a5972ace14ddcd5364968e9bd7b3ad71272defafc9c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0", size = 1655397, upload-time = "2026-03-31T22:00:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/b2/79/c4bbcf4cac3a4715a326e49720ccdc3a4b5e14a367c5029eae7727d06029/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f", size = 1703509, upload-time = "2026-03-31T22:00:45.908Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e6/32d245876f211a7308a7d5437707f9296b1f9837a2888a407ed04e61321c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8", size = 1550098, upload-time = "2026-03-31T22:00:49.48Z" }, - { url = "https://files.pythonhosted.org/packages/db/62/ab0f1304def56ce2356e6fbb9f0b024d6544010351430070f48f53b89e0a/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f", size = 1724326, upload-time = "2026-03-31T22:00:52.165Z" }, - { url = "https://files.pythonhosted.org/packages/c4/9a/aab4469689024046220ea438aa020ea2ae04cd1dd71aea3057e094f8c357/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b", size = 1658824, upload-time = "2026-03-31T22:00:55.122Z" }, - { url = "https://files.pythonhosted.org/packages/b0/98/bcc35d4db687acabf06d41f561a99fa88bca145292513388c858d99b72c5/aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83", size = 440302, upload-time = "2026-03-31T22:00:57.673Z" }, - { url = "https://files.pythonhosted.org/packages/25/61/b0203c2ef6bd268fca0eda142f0efbba7cbebd7ad38f7bb01dd31c2ff68e/aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67", size = 463076, upload-time = "2026-03-31T22:01:00.264Z" }, -] - [[package]] name = "aiohttp" version = "3.14.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "aiohappyeyeballs", version = "2.6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "aiosignal", marker = "python_full_version >= '3.10'" }, - { name = "async-timeout", marker = "python_full_version == '3.10.*'" }, - { name = "attrs", marker = "python_full_version >= '3.10'" }, - { name = "frozenlist", marker = "python_full_version >= '3.10'" }, - { name = "multidict", marker = "python_full_version >= '3.10'" }, - { name = "propcache", version = "0.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, - { name = "yarl", version = "1.24.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "yarl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } wheels = [ @@ -358,9 +188,9 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, - { name = "idna", marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, + { name = "exceptiongroup" }, + { name = "idna" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ @@ -381,9 +211,9 @@ resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ - { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, - { name = "idna", marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" } wheels = [ @@ -408,6 +238,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, ] +[[package]] +name = "botocore" +version = "1.42.97" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/95/c37edb602948fad2253ffd1bb3dba5b938645bd1845ee4160350136a0f41/botocore-1.42.97.tar.gz", hash = "sha256:5c0bb00e32d16ff6d278cc8c9e10dc3672d9c1d569031635ac3c908a60de8310", size = 15269348, upload-time = "2026-04-27T20:39:05.625Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/d2/8e025ba1a4e257879af72d06913272311af79673d82fa2581a351b924317/botocore-1.42.97-py3-none-any.whl", hash = "sha256:77d2c8ce1bc592d3fbd7c01c35836f4a5b0cac2ca03ccdf6ffc60faa16b5fadc", size = 14950367, upload-time = "2026-04-27T20:39:01.261Z" }, +] + +[[package]] +name = "botocore" +version = "1.43.46" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3", version = "2.7.0", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/f1/1917891851ac5ac09bb9f4862b8fc9252a009d7c24e8688bb67e4383d9e7/botocore-1.43.46.tar.gz", hash = "sha256:59f2e1ac3cdc66d191cae91c0804bc41847ce817dc8147cf43eaada8f76a5533", size = 15694635, upload-time = "2026-07-10T19:32:00.437Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/f2/4bd8f2f419088feb3ce55f0ca91040ff902f402edfd197450b20a2e1d533/botocore-1.43.46-py3-none-any.whl", hash = "sha256:cb673891e623ae6e6a1bf24d94ef169504f3eb02584adb5d5bee2f6aae819b60", size = 15380350, upload-time = "2026-07-10T19:31:57.616Z" }, +] + [[package]] name = "certifi" version = "2026.6.17" @@ -535,7 +405,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -722,8 +592,7 @@ name = "httpx-aiohttp" version = "0.1.12" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "aiohttp", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "aiohttp" }, { name = "httpx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/2c/b894861cecf030fb45675ea24aa55b5722e97c602a163d872fca66c5a6d8/httpx_aiohttp-0.1.12.tar.gz", hash = "sha256:81feec51fd82c0ecfa0e9aaf1b1a6c2591260d5e2bcbeb7eb0277a78e610df2c", size = 275945, upload-time = "2025-12-12T10:12:15.283Z" } @@ -856,6 +725,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, ] +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + [[package]] name = "multidict" version = "6.7.1" @@ -1221,7 +1099,7 @@ wheels = [ [[package]] name = "openai" -version = "2.43.0" +version = "2.46.0" source = { editable = "." } dependencies = [ { name = "anyio", version = "4.12.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, @@ -1237,9 +1115,12 @@ dependencies = [ [package.optional-dependencies] aiohttp = [ - { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "aiohttp", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "httpx-aiohttp" }, + { name = "aiohttp", marker = "python_full_version >= '3.10'" }, + { name = "httpx-aiohttp", marker = "python_full_version >= '3.10'" }, +] +bedrock = [ + { name = "botocore", version = "1.42.97", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "botocore", version = "1.43.46", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] datalib = [ { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, @@ -1263,11 +1144,13 @@ voice-helpers = [ [package.metadata] requires-dist = [ - { name = "aiohttp", marker = "extra == 'aiohttp'" }, + { name = "aiohttp", marker = "python_full_version >= '3.10' and extra == 'aiohttp'", specifier = ">=3.14.1" }, { name = "anyio", specifier = ">=3.5.0,<5" }, + { name = "botocore", marker = "python_full_version >= '3.10' and extra == 'bedrock'", specifier = ">=1.40.0,<2" }, + { name = "botocore", marker = "python_full_version < '3.10' and extra == 'bedrock'", specifier = ">=1.40.0,<1.43" }, { name = "distro", specifier = ">=1.7.0,<2" }, { name = "httpx", specifier = ">=0.23.0,<1" }, - { name = "httpx-aiohttp", marker = "extra == 'aiohttp'", specifier = ">=0.1.9" }, + { name = "httpx-aiohttp", marker = "python_full_version >= '3.10' and extra == 'aiohttp'", specifier = ">=0.1.9" }, { name = "jiter", specifier = ">=0.10.0,<1" }, { name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" }, { name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" }, @@ -1280,7 +1163,7 @@ requires-dist = [ { name = "typing-extensions", specifier = ">=4.14,<5" }, { name = "websockets", marker = "extra == 'realtime'", specifier = ">=13,<16" }, ] -provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers"] +provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"] [[package]] name = "pandas" @@ -1291,11 +1174,11 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version != '3.10.*'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "python-dateutil", marker = "python_full_version < '3.11'" }, - { name = "pytz", marker = "python_full_version < '3.11'" }, - { name = "tzdata", marker = "python_full_version < '3.11'" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ @@ -1368,9 +1251,9 @@ resolution-markers = [ "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, - { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } wheels = [ @@ -1431,8 +1314,8 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "types-pytz", version = "2025.2.0.20251108", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" } }, + { name = "types-pytz", version = "2025.2.0.20251108", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/df/0da95bc75c76f1e012e0bc0b76da31faaf4254e94b9870f25e6311145e98/pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93", size = 103095, upload-time = "2024-08-07T12:30:54.538Z" } wheels = [ @@ -1447,8 +1330,8 @@ resolution-markers = [ "python_full_version == '3.10.*'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "types-pytz", version = "2026.2.0.20260518", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "types-pytz", version = "2026.2.0.20260518", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/92/5d/be23854a73fda69f1dbdda7bc10fbd6f930bd1fa87aaec389f00c901c1e8/pandas_stubs-2.3.3.260113.tar.gz", hash = "sha256:076e3724bcaa73de78932b012ec64b3010463d377fa63116f4e6850643d93800", size = 116131, upload-time = "2026-01-13T22:30:16.704Z" } wheels = [ @@ -1468,158 +1351,17 @@ resolution-markers = [ "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/aa/c41a8a0ff86fd85dbb3ec0c1f3fa488ca64a8b5f82654ae1b07d84acefe5/pandas_stubs-3.0.3.260530.tar.gz", hash = "sha256:d1efe47b2e5a312c047d7feabec5cb7a55365747983420077e9fcbe9ab74f714", size = 113183, upload-time = "2026-05-30T17:47:40.34Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0b/e0/99ec5b02203c4e9ce878bc63d8caa06ac1f891e4d63bded9a5ced70fcb4f/pandas_stubs-3.0.3.260530-py3-none-any.whl", hash = "sha256:a6277eb1c8cebf48d9b2413fcd2e9a6b4ff479c934a223c29eacbc3058c4cb55", size = 173780, upload-time = "2026-05-30T17:47:39.13Z" }, ] -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, - { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, - { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, - { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, - { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, - { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, - { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, - { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, - { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, - { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, - { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, - { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, - { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, - { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, - { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, - { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, - { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, - { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, - { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, - { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, - { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, - { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, - { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, - { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, - { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, - { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, - { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, - { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, - { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, - { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, - { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, - { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, - { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - [[package]] name = "propcache" version = "0.5.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*'", -] sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5b/56/030b7b4719d53085722893e0009dffb9236aa10bca1b12121bdc5626ef16/propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b", size = 93417, upload-time = "2026-05-08T20:59:15.597Z" }, @@ -2040,6 +1782,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, ] +[[package]] +name = "urllib3" +version = "1.26.20" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380, upload-time = "2024-08-29T15:43:11.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225, upload-time = "2024-08-29T15:43:08.921Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + [[package]] name = "websockets" version = "15.0.1" @@ -2116,168 +1888,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, ] -[[package]] -name = "yarl" -version = "1.22.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "idna", marker = "python_full_version < '3.10'" }, - { name = "multidict", marker = "python_full_version < '3.10'" }, - { name = "propcache", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, - { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, - { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, - { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, - { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, - { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, - { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, - { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, - { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, - { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, - { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, - { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, - { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, - { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, - { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, - { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, - { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, - { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, - { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, - { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, - { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, - { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, - { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, - { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, - { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, - { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, - { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, - { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, - { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, - { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, - { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, - { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, - { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, - { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, - { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, - { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, - { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, - { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, - { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, - { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, - { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, - { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, - { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, - { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, - { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, - { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, - { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, - { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, - { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, - { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, - { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, - { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, - { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, - { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, - { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, - { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, - { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, - { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, - { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, - { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, - { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, - { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, - { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, - { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, - { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, - { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, - { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, - { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, - { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, - { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, - { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, - { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, - { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, - { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, - { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, - { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, - { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, - { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, - { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, - { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, - { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, - { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, - { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, - { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, - { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, - { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, - { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, - { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, - { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, - { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, - { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, -] - [[package]] name = "yarl" version = "1.24.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", - "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "idna", marker = "python_full_version >= '3.10'" }, - { name = "multidict", marker = "python_full_version >= '3.10'" }, - { name = "propcache", version = "0.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, ] sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } wheels = [ From 7f4edb662abdeb8178c9df48023975684aea4349 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:16:47 +0000 Subject: [PATCH 403/408] feat(stlc): configurable CI runner and private-production-repo support in workflow templates --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91b6ed5e94..2229e3ae02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: lint: timeout-minutes: 10 name: lint - runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -42,7 +42,7 @@ jobs: permissions: contents: read id-token: write - runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -83,7 +83,7 @@ jobs: test: timeout-minutes: 10 name: test - runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 @@ -104,7 +104,7 @@ jobs: timeout-minutes: 10 name: examples environment: ci - runs-on: ${{ github.repository == 'stainless-sdks/openai-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.repository == 'openai/openai-python' && (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: From e1925e9804aa1bbd33359d91ed3fee105cac103f Mon Sep 17 00:00:00 2001 From: Alex Chang Date: Tue, 21 Jul 2026 16:57:23 -0400 Subject: [PATCH 404/408] feat(client): Add experimental runtime support for HTTPX2 clients (#3524) Refs https://github.com/openai/openai-python/issues/3375. ## Summary Adds experimental runtime support for using an HTTPX2 client with the 2.x SDK: ```sh pip install 'openai[httpx2]' ``` ```python from openai import OpenAI, AsyncOpenAI, DefaultHttpx2Client, DefaultAsyncHttpx2Client client = OpenAI(http_client=DefaultHttpx2Client()) async_client = AsyncOpenAI(http_client=DefaultAsyncHttpx2Client()) ``` HTTPX remains installed, imported, and authoritative for the SDK's public transport types. Existing HTTPX clients and helpers continue to work unchanged. This is an explicit runtime opt-in, not a default-transport or typing migration: generated/parsed API models remain accurately typed, while _raw requests, responses, streams, and transport exceptions can be HTTPX2 objects even where annotations still describe HTTPX_. The implementation keeps the compatibility boundary small: - accepts sync, async, and module-level HTTPX2 clients and preserves their native request/response/exception family; - supplies `DefaultHttpx2Client` and `DefaultAsyncHttpx2Client` with the SDK defaults, while retaining the existing HTTPX and aiohttp helpers; - handles the concrete request, timeout, URL, response-casting, retry, streaming, auth, and provider differences needed at runtime; - makes workload-identity token exchange follow an explicitly selected HTTPX2 client (including async clients, whose exchange still runs synchronously in the existing worker-thread path), without changing behavior merely because HTTPX2 happens to be installed. The `httpx2` extra is available on Python 3.10+ and scopes its resolver requirements to the opt-in path (`httpx>=0.25.1,<1`, `httpx2>=2.7,<3`, and `anyio>=4.10,<5`). Base installations retain the existing Python, HTTPX, and AnyIO floors. On Python 3.9 the extra is marker-skipped and invoking an HTTPX2 helper produces an actionable error. Intentionally mixed HTTPX/HTTPX2 transports or auth implementations are out of scope. ## Gaps - This pr does not address `aiohttp`+ `httpx2`; that can be a future addition if there is interest. This would require a separate addon and for us to vendor in some of the implementation; so avoiding in this PR. - Types stay on `httpx`, and `httpx` is still required to be an installed package. - reasoning: we considered `httpx | httpx2` as a migration shim, or returning `httpx2` type annotations. Will continue to evaluate the ecosystem, although `httpx2` types will unfortunately likely require many updates from downstream packages. Saving those considerations for potential future major versions. - For now, we do not address `httpx.timeout` annotations in generated code as well ## Testing The HTTPX2 CI lane runs the normal suite with native HTTPX2 sync/async clients in both Pydantic modes. Existing RESPX-backed cases are exercised through a small, test-only bridge: HTTPX2 uses a native `MockTransport`, the bridge translates only at the RESPX matching/callback boundary, and the SDK still builds and receives native HTTPX2 requests/responses. This keeps generated binary/raw/streaming, retry, auth, Azure, Bedrock, and snapshot coverage shared instead of maintaining duplicate tests; aiohttp remains a separate HTTPX-family path. Focused native tests also cover client defaults, direct injection, raw/SSE/multipart, retries and exception families, hooks/mounts/proxies, provider auth, workload-identity exchange/cache/401 refresh, and base-only/extra resolver behavior. Local full-suite runs pass with HTTPX2/Pydantic 2 and the HTTPX2 floor/Pydantic 1; the ordinary HTTPX suite and lint/type checks remain clean. --- .github/workflows/ci.yml | 40 + README.md | 27 + examples/httpx2_client.py | 10 + pyproject.toml | 5 + scripts/test | 4 + scripts/utils/validate-httpx2-wheel.py | 137 ++++ src/openai/__init__.py | 5 +- src/openai/_base_client.py | 87 ++- src/openai/_client.py | 21 +- src/openai/_httpx2.py | 151 ++++ src/openai/_legacy_response.py | 8 +- src/openai/_response.py | 12 +- src/openai/auth/_workload.py | 6 +- src/openai/lib/azure.py | 5 +- src/openai/lib/streaming/_assistants.py | 11 +- src/openai/providers/bedrock.py | 5 +- .../resources/beta/realtime/realtime.py | 5 +- .../resources/beta/responses/responses.py | 5 +- src/openai/resources/realtime/realtime.py | 5 +- src/openai/resources/responses/responses.py | 5 +- tests/_httpx2_respx.py | 92 +++ tests/conftest.py | 33 +- tests/lib/test_bedrock.py | 27 +- tests/test_client.py | 23 +- tests/test_httpx2.py | 719 ++++++++++++++++++ tests/test_httpx2_base.py | 112 +++ tests/test_httpx2_respx.py | 65 ++ tests/test_httpx2_workload.py | 305 ++++++++ uv.lock | 48 +- 29 files changed, 1892 insertions(+), 86 deletions(-) create mode 100644 examples/httpx2_client.py create mode 100644 scripts/utils/validate-httpx2-wheel.py create mode 100644 src/openai/_httpx2.py create mode 100644 tests/_httpx2_respx.py create mode 100644 tests/test_httpx2.py create mode 100644 tests/test_httpx2_base.py create mode 100644 tests/test_httpx2_respx.py create mode 100644 tests/test_httpx2_workload.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2229e3ae02..2d202ee627 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,6 +61,17 @@ jobs: - name: Validate Bedrock wheel run: rye run python scripts/utils/validate-bedrock-wheel.py + - name: Validate HTTPX2 wheel on Python 3.9 + run: rye run python scripts/utils/validate-httpx2-wheel.py + + - name: Set up Python 3.12 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: '3.12' + + - name: Validate HTTPX2 wheel + run: python scripts/utils/validate-httpx2-wheel.py + - name: Get GitHub OIDC Token if: |- github.repository == 'stainless-sdks/openai-python' && @@ -100,6 +111,35 @@ jobs: - name: Run tests run: ./scripts/test + test-httpx2: + timeout-minutes: 10 + name: test (HTTPX2) + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up Rye + uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 + with: + version: '0.44.0' + enable-cache: true + + - name: Install HTTPX2 test dependencies + run: | + rye pin 3.12 + rye sync --all-features + + - name: Run tests with HTTPX and HTTPX2 installed + env: + OPENAI_TEST_HTTP_CLIENT: httpx + run: ./scripts/test + + - name: Run tests with HTTPX2 + env: + OPENAI_TEST_HTTP_CLIENT: httpx2 + run: ./scripts/test + examples: timeout-minutes: 10 name: examples diff --git a/README.md b/README.md index dd6c9d968d..9ca8fc90a4 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,33 @@ async def main() -> None: asyncio.run(main()) ``` +### Experimental HTTPX2 support + +To opt in to experimental HTTPX2 support, install the optional extra on Python 3.10 or later: + +```sh +pip install 'openai[httpx2]' +``` + +```python +from openai import OpenAI, AsyncOpenAI, DefaultHttpx2Client, DefaultAsyncHttpx2Client + +client = OpenAI(http_client=DefaultHttpx2Client()) +async_client = AsyncOpenAI(http_client=DefaultAsyncHttpx2Client()) +``` + +See [`examples/httpx2_client.py`](examples/httpx2_client.py) for a minimal runnable example. + +The module-level client can be configured in the same way: + +```python +import openai + +openai.http_client = openai.DefaultHttpx2Client() +``` + +Parsed API models are unchanged, but requests, raw and streaming responses, and transport-level exceptions may be HTTPX2 objects at runtime. Code that catches HTTPX exceptions or relies on HTTPX-specific mocks, transports, authentication, hooks, or instrumentation may need to be updated. Transport-facing type annotations may still describe HTTPX. + ## Streaming responses We provide support for streaming responses using Server Side Events (SSE). diff --git a/examples/httpx2_client.py b/examples/httpx2_client.py new file mode 100644 index 0000000000..eb4ab08f87 --- /dev/null +++ b/examples/httpx2_client.py @@ -0,0 +1,10 @@ +from openai import OpenAI, DefaultHttpx2Client + +client = OpenAI(http_client=DefaultHttpx2Client()) + +response = client.responses.create( + model="gpt-5.2", + input="Say this is a test", +) + +print(response.output_text) diff --git a/pyproject.toml b/pyproject.toml index 0256365772..7b8fae090d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,11 @@ aiohttp = [ "aiohttp>=3.14.1; python_version >= '3.10'", "httpx_aiohttp>=0.1.9; python_version >= '3.10'", ] +httpx2 = [ + "httpx>=0.25.1, <1; python_version >= '3.10'", + "httpx2>=2.7.0, <3; python_version >= '3.10'", + "anyio>=4.10.0, <5; python_version >= '3.10'", +] realtime = ["websockets >= 13, < 16"] datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"] voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"] diff --git a/scripts/test b/scripts/test index 7b05e44fd9..daff369239 100755 --- a/scripts/test +++ b/scripts/test @@ -54,6 +54,10 @@ fi export DEFER_PYDANTIC_BUILD=false +if [ "${OPENAI_TEST_HTTP_CLIENT:-httpx}" = "httpx2" ]; then + echo "==> Using HTTPX2 for sync and async API-resource clients (including RESPX-backed cases)" +fi + echo "==> Running tests" rye run pytest "$@" diff --git a/scripts/utils/validate-httpx2-wheel.py b/scripts/utils/validate-httpx2-wheel.py new file mode 100644 index 0000000000..681e443d4c --- /dev/null +++ b/scripts/utils/validate-httpx2-wheel.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +import os +import sys +import email +import zipfile +import tempfile +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +BASE_TEST = ROOT / "tests/test_httpx2_base.py" +HTTPX2_TEST = ROOT / "tests/test_httpx2.py" + + +def validate_metadata(wheel: Path) -> None: + with zipfile.ZipFile(wheel) as archive: + metadata_names = [name for name in archive.namelist() if name.endswith(".dist-info/METADATA")] + if len(metadata_names) != 1: + raise RuntimeError(f"Expected exactly one METADATA file in {wheel}, found: {metadata_names}") + metadata = email.message_from_bytes(archive.read(metadata_names[0])) + + requirements = metadata.get_all("Requires-Dist", []) + base = [value for value in requirements if "extra ==" not in value] + httpx2 = [value for value in requirements if "extra == 'httpx2'" in value] + aiohttp = [value for value in requirements if "extra == 'aiohttp'" in value] + + if metadata["Requires-Python"] != ">=3.9": + raise RuntimeError(f"Expected Python >=3.9, found: {metadata['Requires-Python']}") + if not any(value.startswith("httpx<1,>=0.23.0") for value in base): + raise RuntimeError(f"Expected the base wheel to require HTTPX >=0.23.0,<1: {base}") + if not any(value.startswith("anyio<5,>=3.5.0") for value in base): + raise RuntimeError(f"Expected the base wheel to require AnyIO >=3.5.0,<5: {base}") + if any(value.startswith("httpx2") for value in base): + raise RuntimeError(f"HTTPX2 leaked into the base wheel requirements: {base}") + + for expected in ("httpx<1,>=0.25.1", "httpx2<3,>=2.7.0", "anyio<5,>=4.10.0"): + if not any(value.startswith(expected) for value in httpx2): + raise RuntimeError(f"Expected the HTTPX2 extra to require {expected}: {httpx2}") + if not all("python_version >= '3.10'" in value for value in httpx2): + raise RuntimeError(f"HTTPX2 requirements must be marked for Python >=3.10: {httpx2}") + + if not any(value.startswith("aiohttp>=3.14.1") for value in aiohttp): + raise RuntimeError(f"Expected the unchanged aiohttp requirement: {aiohttp}") + if not any(value.startswith("httpx-aiohttp>=0.1.9") for value in aiohttp): + raise RuntimeError(f"Expected the unchanged httpx-aiohttp requirement: {aiohttp}") + + +def run_case(wheel: Path, *, extra: str | None, tests: list[Path], dependencies: list[str]) -> None: + with tempfile.TemporaryDirectory(prefix="openai-httpx2-wheel-") as directory: + environment_path = Path(directory) / "venv" + subprocess.run([sys.executable, "-m", "venv", str(environment_path)], check=True) + python = environment_path / "bin/python" + requirement = str(wheel.resolve()) if extra is None else f"{wheel.resolve()}[{extra}]" + environment = os.environ.copy() + environment.setdefault("PIP_DISABLE_CLIENT_CERTIFICATE", "1") + subprocess.run( + [str(python), "-m", "pip", "install", "--quiet", requirement, *dependencies], + cwd=directory, + env=environment, + check=True, + ) + subprocess.run( + [str(python), "-m", "pytest", "-o", "addopts=", *(str(test) for test in tests)], + cwd=directory, + env=environment, + check=True, + ) + + +def assert_incompatible_pin_fails(wheel: Path) -> None: + with tempfile.TemporaryDirectory(prefix="openai-httpx2-conflict-") as directory: + environment_path = Path(directory) / "venv" + subprocess.run([sys.executable, "-m", "venv", str(environment_path)], check=True) + python = environment_path / "bin/python" + result = subprocess.run( + [ + str(python), + "-m", + "pip", + "install", + "--dry-run", + f"{wheel.resolve()}[httpx2]", + "httpx==0.25.0", + ], + cwd=directory, + env=os.environ.copy(), + capture_output=True, + text=True, + check=False, + ) + if result.returncode == 0: + raise RuntimeError("Expected openai[httpx2] with httpx==0.25.0 to fail dependency resolution") + output = result.stdout + result.stderr + if "ResolutionImpossible" not in output and "conflicting dependencies" not in output: + raise RuntimeError(f"The incompatible resolution failed for an unexpected reason:\n{output}") + + +def supports_httpx2() -> bool: + return sys.version_info >= (3, 10) + + +def main() -> None: + wheels = list((ROOT / "dist").glob("*.whl")) + if len(wheels) != 1: + raise RuntimeError(f"Expected exactly one wheel in dist/, found: {wheels}") + wheel = wheels[0] + validate_metadata(wheel) + + common = ["pytest==8.4.1", "pytest-asyncio==1.1.0", "respx==0.22.0"] + run_case(wheel, extra=None, tests=[BASE_TEST], dependencies=common) + run_case( + wheel, + extra=None, + tests=[BASE_TEST], + dependencies=["pytest==8.4.1", "pytest-asyncio==1.1.0", "respx==0.20.2", "httpx==0.23.0", "anyio==3.5.0"], + ) + + if not supports_httpx2(): + run_case(wheel, extra="httpx2", tests=[BASE_TEST], dependencies=common) + print("Validated base HTTPX, RESPX, supported floors, and the Python 3.9 HTTPX2 marker") + return + + run_case(wheel, extra="aiohttp", tests=[BASE_TEST], dependencies=common) + run_case(wheel, extra="httpx2", tests=[BASE_TEST, HTTPX2_TEST], dependencies=common) + run_case( + wheel, + extra="httpx2", + tests=[BASE_TEST, HTTPX2_TEST], + dependencies=[*common, "httpx==0.25.1", "anyio==4.10.0", "pydantic<2", "botocore==1.42.97"], + ) + assert_incompatible_pin_fails(wheel) + print("Validated base, aiohttp, native HTTPX2, supported floors, Pydantic modes, and resolver conflicts") + + +if __name__ == "__main__": + main() diff --git a/src/openai/__init__.py b/src/openai/__init__.py index a3f3237c38..075a03a964 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -10,6 +10,7 @@ from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path from ._client import Client, OpenAI, Stream, Timeout, Transport, AsyncClient, AsyncOpenAI, AsyncStream, RequestOptions +from ._httpx2 import DefaultHttpx2Client, DefaultAsyncHttpx2Client, normalize_httpx_url as _normalize_httpx_url from ._models import BaseModel from ._version import __title__, __version__ from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse @@ -89,6 +90,8 @@ "DefaultHttpxClient", "DefaultAsyncHttpxClient", "DefaultAioHttpClient", + "DefaultHttpx2Client", + "DefaultAsyncHttpx2Client", "ReconnectingEvent", "ReconnectingOverrides", "WebSocketQueueFullError", @@ -233,7 +236,7 @@ def webhook_secret(self, value: str | None) -> None: # type: ignore @override def base_url(self) -> _httpx.URL: if base_url is not None: - return _httpx.URL(base_url) + return _normalize_httpx_url(base_url) return super().base_url diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 4933c8e2fe..3d7dda5afd 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -63,6 +63,16 @@ ) from ._utils import SensitiveHeadersFilter, is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import PYDANTIC_V1, model_copy, model_dump +from ._httpx2 import ( + status_exceptions, + timeout_exceptions, + normalize_httpx_url, + is_httpx2_sync_client, + normalize_httpx2_auth, + is_httpx2_async_client, + normalize_httpx_timeout, + normalize_httpx2_timeout, +) from ._models import GenericModel, SecurityOptions, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -385,7 +395,7 @@ def __init__( custom_query: Mapping[str, object] | None = None, ) -> None: self._version = version - self._base_url = self._enforce_trailing_slash(URL(base_url)) + self._base_url = self._enforce_trailing_slash(normalize_httpx_url(base_url)) self.max_retries = max_retries self.timeout = timeout self._custom_headers = custom_headers or {} @@ -471,7 +481,9 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 if "x-stainless-retry-count" not in lower_custom_headers: headers["x-stainless-retry-count"] = str(retries_taken) if "x-stainless-read-timeout" not in lower_custom_headers: - timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + timeout = normalize_httpx_timeout( + self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + ) if isinstance(timeout, Timeout): timeout = timeout.read if timeout is not None: @@ -589,12 +601,20 @@ def _build_request( headers.pop("Content-Type", None) kwargs.pop("data", None) + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + request_url: str | URL = prepared_url + request_headers: httpx.Headers | list[tuple[str, str]] = headers + if is_httpx2_sync_client(self._client) or is_httpx2_async_client(self._client): + request_url = str(prepared_url) + request_headers = list(headers.multi_items()) + timeout = normalize_httpx2_timeout(timeout) + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] - headers=headers, - timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, + headers=request_headers, + timeout=timeout, method=options.method, - url=prepared_url, + url=request_url, # the `Query` type that we use is incompatible with qs' # `Params` type as it needs to be typed as `Mapping[str, object]` # so that passing a `TypedDict` doesn't cause an error. @@ -726,7 +746,7 @@ def base_url(self) -> URL: @base_url.setter def base_url(self, url: URL | str) -> None: - self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) + self._base_url = self._enforce_trailing_slash(normalize_httpx_url(url)) def platform_headers(self) -> Dict[str, str]: # the actual implementation is in a separate `lru_cache` decorated @@ -886,14 +906,20 @@ def __init__( # where they've explicitly set the timeout to match the default timeout # as this check is structural, meaning that we'll think they didn't # pass in a timeout and will ignore it - if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: - timeout = http_client.timeout + client_timeout = normalize_httpx_timeout(http_client.timeout) if http_client else None + if http_client and client_timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = client_timeout else: timeout = DEFAULT_TIMEOUT - if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + if ( + http_client is not None + and not is_httpx2_sync_client(http_client) + and not isinstance(http_client, httpx.Client) # pyright: ignore[reportUnnecessaryIsInstance] + ): raise TypeError( - f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" + "Invalid `http_client` argument; Expected an instance of `httpx.Client` or `httpx2.Client` " + f"but got {type(http_client)}" ) super().__init__( @@ -1025,7 +1051,9 @@ def request( kwargs: HttpxSendArgs = {} custom_auth = self._custom_auth(options.security) if custom_auth is not None: - kwargs["auth"] = custom_auth + kwargs["auth"] = ( + normalize_httpx2_auth(custom_auth) if is_httpx2_sync_client(self._client) else custom_auth + ) if options.follow_redirects is not None: kwargs["follow_redirects"] = options.follow_redirects @@ -1039,8 +1067,8 @@ def request( stream=stream or self._should_stream_response_body(request=request), **kwargs, ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) + except timeout_exceptions() as err: + log.debug("Encountered a timeout exception", exc_info=True) if remaining_retries > 0: self._sleep_for_retry( @@ -1083,8 +1111,8 @@ def request( try: response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + except status_exceptions() as err: # thrown on 4xx and 5xx status code + log.debug("Encountered an HTTP status error", exc_info=True) if remaining_retries > 0 and self._should_retry(err.response): err.response.close() @@ -1427,6 +1455,7 @@ def __init__(self, **kwargs: Any) -> None: if sys.version_info < (3, 10): + class _DefaultAioHttpClient(httpx.AsyncClient): def __init__(self, **_kwargs: Any) -> None: raise RuntimeError("The aiohttp client requires Python 3.10 or later") @@ -1503,14 +1532,20 @@ def __init__( # where they've explicitly set the timeout to match the default timeout # as this check is structural, meaning that we'll think they didn't # pass in a timeout and will ignore it - if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: - timeout = http_client.timeout + client_timeout = normalize_httpx_timeout(http_client.timeout) if http_client else None + if http_client and client_timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = client_timeout else: timeout = DEFAULT_TIMEOUT - if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + if ( + http_client is not None + and not is_httpx2_async_client(http_client) + and not isinstance(http_client, httpx.AsyncClient) # pyright: ignore[reportUnnecessaryIsInstance] + ): raise TypeError( - f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" + "Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` or " + f"`httpx2.AsyncClient` but got {type(http_client)}" ) super().__init__( @@ -1643,7 +1678,11 @@ async def request( kwargs: HttpxSendArgs = {} if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + kwargs["auth"] = ( + normalize_httpx2_auth(self.custom_auth) + if is_httpx2_async_client(self._client) + else self.custom_auth + ) if options.follow_redirects is not None: kwargs["follow_redirects"] = options.follow_redirects @@ -1657,8 +1696,8 @@ async def request( stream=stream or self._should_stream_response_body(request=request), **kwargs, ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) + except timeout_exceptions() as err: + log.debug("Encountered a timeout exception", exc_info=True) if remaining_retries > 0: await self._sleep_for_retry( @@ -1701,8 +1740,8 @@ async def request( try: response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + except status_exceptions() as err: # thrown on 4xx and 5xx status code + log.debug("Encountered an HTTP status error", exc_info=True) if remaining_retries > 0 and self._should_retry(err.response): await err.response.aclose() diff --git a/src/openai/_client.py b/src/openai/_client.py index 66d03b23dd..aeea9907a8 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -29,6 +29,7 @@ get_async_library, ) from ._compat import cached_property +from ._httpx2 import is_httpx2_sync_client, is_httpx2_async_client from ._models import SecurityOptions, FinalRequestOptions from ._version import __version__ from ._provider import _Provider, _provider_name, _ProviderRuntime, _configure_provider @@ -201,9 +202,7 @@ def __init__( elif workload_identity is not None: self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER self._api_key_provider = None - self._workload_identity_auth = WorkloadIdentityAuth( - workload_identity=workload_identity, - ) + self._workload_identity_auth = None else: if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") @@ -272,6 +271,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + if workload_identity is not None: + self._workload_identity_auth = WorkloadIdentityAuth( + workload_identity=workload_identity, + _use_httpx2=is_httpx2_sync_client(self._client), + ) + self._default_stream_cls = Stream @cached_property @@ -797,9 +802,7 @@ def __init__( elif workload_identity is not None: self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER self._api_key_provider = None - self._workload_identity_auth = WorkloadIdentityAuth( - workload_identity=workload_identity, - ) + self._workload_identity_auth = None else: if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") @@ -868,6 +871,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + if workload_identity is not None: + self._workload_identity_auth = WorkloadIdentityAuth( + workload_identity=workload_identity, + _use_httpx2=is_httpx2_async_client(self._client), + ) + self._default_stream_cls = AsyncStream @cached_property diff --git a/src/openai/_httpx2.py b/src/openai/_httpx2.py new file mode 100644 index 0000000000..7dd3d8d6d9 --- /dev/null +++ b/src/openai/_httpx2.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +import sys +import importlib +from typing import Any, Protocol, cast + +import httpx + +from ._constants import DEFAULT_TIMEOUT, DEFAULT_CONNECTION_LIMITS + + +class _Httpx2Module(Protocol): + Auth: type[httpx.Auth] + Client: type[httpx.Client] + AsyncClient: type[httpx.AsyncClient] + URL: type[httpx.URL] + Response: type[httpx.Response] + Timeout: type[httpx.Timeout] + Limits: type[httpx.Limits] + TimeoutException: type[httpx.TimeoutException] + HTTPStatusError: type[httpx.HTTPStatusError] + StreamConsumed: type[httpx.StreamConsumed] + RequestNotRead: type[httpx.RequestNotRead] + + +def _loaded_httpx2() -> _Httpx2Module | None: + module = sys.modules.get("httpx2") + if module is None: + return None + return cast(_Httpx2Module, module) + + +def _supports_httpx2() -> bool: + return sys.version_info >= (3, 10) + + +def _require_httpx2() -> _Httpx2Module: + if not _supports_httpx2(): + raise RuntimeError( + "HTTPX2 requires Python 3.10 or later; install the httpx2 extra on a supported interpreter: " + "pip install 'openai[httpx2]'" + ) + + try: + module = importlib.import_module("httpx2") + except ImportError: + raise RuntimeError("To use HTTPX2, install the httpx2 extra: pip install 'openai[httpx2]'") from None + + return cast(_Httpx2Module, module) + + +def is_httpx2_sync_client(value: object) -> bool: + module = _loaded_httpx2() + return module is not None and isinstance(value, module.Client) + + +def is_httpx2_async_client(value: object) -> bool: + module = _loaded_httpx2() + return module is not None and isinstance(value, module.AsyncClient) + + +def normalize_httpx_url(value: str | httpx.URL) -> httpx.URL: + module = _loaded_httpx2() + if module is not None and isinstance(value, module.URL): + return httpx.URL(str(value)) + if isinstance(value, httpx.URL): + return value + return httpx.URL(value) + + +def http_response_types() -> tuple[type[httpx.Response], ...]: + module = _loaded_httpx2() + if module is None: + return (httpx.Response,) + return (httpx.Response, module.Response) + + +def normalize_httpx_timeout(value: float | httpx.Timeout | None) -> float | httpx.Timeout | None: + module = _loaded_httpx2() + if module is not None and isinstance(value, module.Timeout): + return httpx.Timeout(**value.as_dict()) + return value + + +def normalize_httpx2_timeout(value: float | httpx.Timeout | None) -> float | httpx.Timeout | None: + if isinstance(value, httpx.Timeout): + return _require_httpx2().Timeout(**value.as_dict()) + return value + + +def normalize_httpx2_auth(value: httpx.Auth) -> httpx.Auth: + if type(value) is httpx.Auth: + return _require_httpx2().Auth() + return value + + +def timeout_exceptions() -> tuple[type[httpx.TimeoutException], ...]: + module = _loaded_httpx2() + if module is None: + return (httpx.TimeoutException,) + return (httpx.TimeoutException, module.TimeoutException) + + +def status_exceptions() -> tuple[type[httpx.HTTPStatusError], ...]: + module = _loaded_httpx2() + if module is None: + return (httpx.HTTPStatusError,) + return (httpx.HTTPStatusError, module.HTTPStatusError) + + +def stream_consumed_exceptions() -> tuple[type[httpx.StreamConsumed], ...]: + module = _loaded_httpx2() + if module is None: + return (httpx.StreamConsumed,) + return (httpx.StreamConsumed, module.StreamConsumed) + + +def request_not_read_exceptions() -> tuple[type[httpx.RequestNotRead], ...]: + module = _loaded_httpx2() + if module is None: + return (httpx.RequestNotRead,) + return (httpx.RequestNotRead, module.RequestNotRead) + + +def _set_httpx2_defaults(kwargs: dict[str, Any]) -> _Httpx2Module: + module = _require_httpx2() + timeout = kwargs.get("timeout", DEFAULT_TIMEOUT) + kwargs["timeout"] = normalize_httpx2_timeout(timeout) + + limits = kwargs.get("limits", DEFAULT_CONNECTION_LIMITS) + if isinstance(limits, httpx.Limits): + kwargs["limits"] = module.Limits( + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + ) + + kwargs.setdefault("follow_redirects", True) + return module + + +def DefaultHttpx2Client(**kwargs: Any) -> httpx.Client: + """Create an experimental HTTPX2 client with the SDK's recommended defaults.""" + module = _set_httpx2_defaults(kwargs) + return module.Client(**kwargs) + + +def DefaultAsyncHttpx2Client(**kwargs: Any) -> httpx.AsyncClient: + """Create an experimental async HTTPX2 client with the SDK's recommended defaults.""" + module = _set_httpx2_defaults(kwargs) + return module.AsyncClient(**kwargs) diff --git a/src/openai/_legacy_response.py b/src/openai/_legacy_response.py index 1a58c2dfc3..542bd6c660 100644 --- a/src/openai/_legacy_response.py +++ b/src/openai/_legacy_response.py @@ -25,6 +25,7 @@ from ._types import NoneType from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type +from ._httpx2 import http_response_types from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -272,16 +273,17 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if origin == LegacyAPIResponse: raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + response_types = http_response_types() if inspect.isclass( origin # pyright: ignore[reportUnknownArgumentType] - ) and issubclass(origin, httpx.Response): + ) and issubclass(origin, response_types): # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response # and pass that class to our request functions. We cannot change the variance to be either # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct # the response class ourselves but that is something that should be supported directly in httpx # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. - if cast_to != httpx.Response: - raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + if cast_to not in response_types: + raise ValueError("Subclasses of HTTP response classes cannot be passed to `cast_to`") return cast(R, response) if ( diff --git a/src/openai/_response.py b/src/openai/_response.py index f286d38e6c..5a790d69a2 100644 --- a/src/openai/_response.py +++ b/src/openai/_response.py @@ -26,6 +26,7 @@ from ._types import NoneType from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base +from ._httpx2 import http_response_types, stream_consumed_exceptions from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type @@ -207,14 +208,15 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: if origin == APIResponse: raise RuntimeError("Unexpected state - cast_to is `APIResponse`") - if inspect.isclass(origin) and issubclass(origin, httpx.Response): + response_types = http_response_types() + if inspect.isclass(origin) and issubclass(origin, response_types): # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response # and pass that class to our request functions. We cannot change the variance to be either # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct # the response class ourselves but that is something that should be supported directly in httpx # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. - if cast_to != httpx.Response: - raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + if cast_to not in response_types: + raise ValueError("Subclasses of HTTP response classes cannot be passed to `cast_to`") return cast(R, response) if ( @@ -337,7 +339,7 @@ def read(self) -> bytes: """Read and return the binary response content.""" try: return self.http_response.read() - except httpx.StreamConsumed as exc: + except stream_consumed_exceptions() as exc: # The default error raised by httpx isn't very # helpful in our case so we re-raise it with # a different error message. @@ -444,7 +446,7 @@ async def read(self) -> bytes: """Read and return the binary response content.""" try: return await self.http_response.aread() - except httpx.StreamConsumed as exc: + except stream_consumed_exceptions() as exc: # the default error raised by httpx isn't very # helpful in our case so we re-raise it with # a different error message diff --git a/src/openai/auth/_workload.py b/src/openai/auth/_workload.py index 6b13ededb2..1c1efe536d 100644 --- a/src/openai/auth/_workload.py +++ b/src/openai/auth/_workload.py @@ -8,6 +8,7 @@ import httpx +from .._httpx2 import DefaultHttpx2Client from .._exceptions import OAuthError, OpenAIError, SubjectTokenProviderError from .._utils._sync import to_thread @@ -176,9 +177,11 @@ def __init__( *, workload_identity: WorkloadIdentity, token_exchange_url: str = DEFAULT_TOKEN_EXCHANGE_URL, + _use_httpx2: bool = False, ): self.workload_identity = workload_identity self.token_exchange_url = token_exchange_url + self._use_httpx2 = _use_httpx2 self._cached_token: str | None = None self._cached_token_expires_at_monotonic: float | None = None @@ -245,7 +248,8 @@ def _fetch_token_from_exchange(self) -> dict[str, Any]: f"Unsupported token type: {token_type!r}. Supported types: {', '.join(SUBJECT_TOKEN_TYPES.keys())}" ) - with httpx.Client() as client: + exchange_client = DefaultHttpx2Client(follow_redirects=False) if self._use_httpx2 else httpx.Client() + with exchange_client as client: response = client.post( self.token_exchange_url, json={ diff --git a/src/openai/lib/azure.py b/src/openai/lib/azure.py index 888b480dbb..4ebe0a98aa 100644 --- a/src/openai/lib/azure.py +++ b/src/openai/lib/azure.py @@ -12,6 +12,7 @@ from .._utils import is_given, is_mapping from .._client import OpenAI, AsyncOpenAI from .._compat import model_copy +from .._httpx2 import normalize_httpx_url from .._models import SecurityOptions, FinalRequestOptions from .._provider import _Provider from .._streaming import Stream, AsyncStream @@ -407,7 +408,7 @@ def _configure_realtime(self, model: str, extra_query: Query) -> tuple[httpx.URL auth_headers = {"Authorization": f"Bearer {token}"} if self.websocket_base_url is not None: - base_url = httpx.URL(self.websocket_base_url) + base_url = normalize_httpx_url(self.websocket_base_url) merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" realtime_url = base_url.copy_with(raw_path=merge_raw_path) else: @@ -733,7 +734,7 @@ async def _configure_realtime(self, model: str, extra_query: Query) -> tuple[htt auth_headers = {"Authorization": f"Bearer {token}"} if self.websocket_base_url is not None: - base_url = httpx.URL(self.websocket_base_url) + base_url = normalize_httpx_url(self.websocket_base_url) merge_raw_path = base_url.raw_path.rstrip(b"/") + b"/realtime" realtime_url = base_url.copy_with(raw_path=merge_raw_path) else: diff --git a/src/openai/lib/streaming/_assistants.py b/src/openai/lib/streaming/_assistants.py index 6efb3ca3f1..314961230d 100644 --- a/src/openai/lib/streaming/_assistants.py +++ b/src/openai/lib/streaming/_assistants.py @@ -5,10 +5,9 @@ from typing import TYPE_CHECKING, Any, Generic, TypeVar, Callable, Iterable, Iterator, cast from typing_extensions import Awaitable, AsyncIterable, AsyncIterator, assert_never -import httpx - from ..._utils import is_dict, is_list, consume_sync_iterator, consume_async_iterator from ..._compat import model_dump +from ..._httpx2 import timeout_exceptions from ..._models import construct_type from ..._streaming import Stream, AsyncStream from ...types.beta import AssistantStreamEvent @@ -25,6 +24,10 @@ from ...types.beta.threads.runs import RunStep, ToolCall, RunStepDelta, ToolCallDelta +def _timeout_exceptions() -> tuple[type[Exception], ...]: + return (*timeout_exceptions(), asyncio.TimeoutError) + + class AssistantEventHandler: text_deltas: Iterable[str] """Iterator over just the text deltas in the stream. @@ -407,7 +410,7 @@ def __stream__(self) -> Iterator[AssistantStreamEvent]: self._emit_sse_event(event) yield event - except (httpx.TimeoutException, asyncio.TimeoutError) as exc: + except _timeout_exceptions() as exc: self.on_timeout() self.on_exception(exc) raise @@ -839,7 +842,7 @@ async def __stream__(self) -> AsyncIterator[AssistantStreamEvent]: await self._emit_sse_event(event) yield event - except (httpx.TimeoutException, asyncio.TimeoutError) as exc: + except _timeout_exceptions() as exc: await self.on_timeout() await self.on_exception(exc) raise diff --git a/src/openai/providers/bedrock.py b/src/openai/providers/bedrock.py index 5cee093bfd..ed6be81c9e 100644 --- a/src/openai/providers/bedrock.py +++ b/src/openai/providers/bedrock.py @@ -10,6 +10,7 @@ from .._types import NOT_GIVEN, NotGiven from .._utils import asyncify +from .._httpx2 import normalize_httpx_url, request_not_read_exceptions from .._models import FinalRequestOptions from .._provider import _Provider, _create_provider, _ProviderRuntime from .._exceptions import OpenAIError @@ -34,7 +35,7 @@ def _normalize_optional_string(value: str | None) -> str | None: def _normalize_base_url(base_url: str | httpx.URL) -> httpx.URL: - url = httpx.URL(base_url) + url = normalize_httpx_url(base_url) path = url.path.rstrip("/") responses_match = re.search(r"/responses(?:/.*)?$", path) if responses_match is not None: @@ -50,7 +51,7 @@ def _same_origin(left: httpx.URL, right: httpx.URL) -> bool: def _body_for_signing(request: httpx.Request) -> bytes: try: return request.content - except httpx.RequestNotRead as exc: + except request_not_read_exceptions() as exc: raise OpenAIError( "Bedrock SigV4 authentication requires a replayable request body. " "Buffer the body before sending or use bearer authentication." diff --git a/src/openai/resources/beta/realtime/realtime.py b/src/openai/resources/beta/realtime/realtime.py index 4fa35963b6..98b717a184 100644 --- a/src/openai/resources/beta/realtime/realtime.py +++ b/src/openai/resources/beta/realtime/realtime.py @@ -28,6 +28,7 @@ is_async_azure_client, ) from ...._compat import cached_property +from ...._httpx2 import normalize_httpx_url from ...._models import construct_type_unchecked from ...._resource import SyncAPIResource, AsyncAPIResource from ...._exceptions import OpenAIError @@ -395,7 +396,7 @@ async def __aenter__(self) -> AsyncRealtimeConnection: def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: base_url = self.__client._base_url.copy_with(scheme="wss") @@ -578,7 +579,7 @@ def __enter__(self) -> RealtimeConnection: def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: base_url = self.__client._base_url.copy_with(scheme="wss") diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py index 5ac018f744..4ec4723d20 100644 --- a/src/openai/resources/beta/responses/responses.py +++ b/src/openai/resources/beta/responses/responses.py @@ -17,6 +17,7 @@ from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property +from ...._httpx2 import normalize_httpx_url from ...._models import construct_type_unchecked from .input_items import ( InputItems, @@ -4395,7 +4396,7 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: scheme = self.__client._base_url.scheme ws_scheme = "ws" if scheme == "http" else "wss" @@ -4840,7 +4841,7 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: scheme = self.__client._base_url.scheme ws_scheme = "ws" if scheme == "http" else "wss" diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index e4c5bd8163..906884063c 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -30,6 +30,7 @@ is_async_azure_client, ) from ..._compat import cached_property +from ..._httpx2 import normalize_httpx_url from ..._models import construct_type_unchecked from ..._resource import SyncAPIResource, AsyncAPIResource from ..._exceptions import OpenAIError, WebSocketConnectionClosedError @@ -721,7 +722,7 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: scheme = self.__client._base_url.scheme ws_scheme = "ws" if scheme == "http" else "wss" @@ -1189,7 +1190,7 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: scheme = self.__client._base_url.scheme ws_scheme = "ws" if scheme == "http" else "wss" diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 8131b57ca2..4fcf33a257 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -32,6 +32,7 @@ from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property +from ..._httpx2 import normalize_httpx_url from ..._models import construct_type_unchecked from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -4346,7 +4347,7 @@ async def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> Async def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: scheme = self.__client._base_url.scheme ws_scheme = "ws" if scheme == "http" else "wss" @@ -4791,7 +4792,7 @@ def _connect_ws(self, extra_query: Query, extra_headers: Headers) -> WebSocketCo def _prepare_url(self) -> httpx.URL: if self.__client.websocket_base_url is not None: - base_url = httpx.URL(self.__client.websocket_base_url) + base_url = normalize_httpx_url(self.__client.websocket_base_url) else: scheme = self.__client._base_url.scheme ws_scheme = "ws" if scheme == "http" else "wss" diff --git a/tests/_httpx2_respx.py b/tests/_httpx2_respx.py new file mode 100644 index 0000000000..3b66323daf --- /dev/null +++ b/tests/_httpx2_respx.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import os +from typing import Any, NoReturn + +import httpx +import pytest +from respx import MockRouter + +from openai import DefaultHttpx2Client, DefaultAsyncHttpx2Client + + +def httpx2_enabled() -> bool: + return os.environ.get("OPENAI_TEST_HTTP_CLIENT") == "httpx2" + + +def sync_http_client(**kwargs: Any) -> httpx.Client: + if httpx2_enabled(): + return DefaultHttpx2Client(**kwargs) + return httpx.Client(**kwargs) + + +def async_http_client(**kwargs: Any) -> httpx.AsyncClient: + if httpx2_enabled(): + return DefaultAsyncHttpx2Client(**kwargs) + return httpx.AsyncClient(**kwargs) + + +def enable_httpx2_respx( + router: MockRouter, monkeypatch: pytest.MonkeyPatch, *, replace_sdk_defaults: bool = True +) -> None: + httpx2 = pytest.importorskip("httpx2") + + # RESPX deliberately only understands HTTPX objects. Keep the SDK side native and + # translate at this single test-only boundary so existing routes, callbacks, and + # call assertions can be exercised against HTTPX2 without copied test cases. + def httpx_request(native_request: Any, *, content: bytes) -> httpx.Request: + return httpx.Request( + native_request.method, + str(native_request.url), + headers=list(native_request.headers.multi_items()), + content=content, + extensions=dict(native_request.extensions), + ) + + def httpx2_response(response: httpx.Response, *, native_request: Any, content: bytes) -> Any: + return httpx2.Response( + response.status_code, + headers=list(response.headers.multi_items()), + # Supplying a stream keeps streaming-response tests meaningful: the native + # response stays open until the SDK consumes or closes it. + stream=httpx2.ByteStream(content), + request=native_request, + extensions=dict(response.extensions), + ) + + def raise_native_request_error(exc: httpx.RequestError, native_request: Any) -> NoReturn: + native_error = getattr(httpx2, type(exc).__name__, httpx2.RequestError) + raise native_error(str(exc), request=native_request) from exc + + def handler(native_request: Any) -> Any: + try: + response = router.handler(httpx_request(native_request, content=native_request.read())) + except httpx.RequestError as exc: + raise_native_request_error(exc, native_request) + return httpx2_response(response, native_request=native_request, content=response.read()) + + async def async_handler(native_request: Any) -> Any: + try: + response = await router.async_handler(httpx_request(native_request, content=await native_request.aread())) + except httpx.RequestError as exc: + raise_native_request_error(exc, native_request) + return httpx2_response(response, native_request=native_request, content=await response.aread()) + + # Match RESPX's own patch point. This also catches clients created inside a test, + # while leaving every non-RESPX test on its ordinary transport. + def sync_transport(*_args: Any) -> Any: + return httpx2.MockTransport(handler) + + def async_transport(*_args: Any) -> Any: + return httpx2.MockTransport(async_handler) + + monkeypatch.setattr(httpx2.Client, "_transport_for_url", sync_transport) + monkeypatch.setattr(httpx2.AsyncClient, "_transport_for_url", async_transport) + + if replace_sdk_defaults: + import openai._base_client as base_client + + # Clients constructed inside a RESPX test should exercise the same native + # path as the shared fixtures; the base-compatibility tests opt out. + monkeypatch.setattr(base_client, "SyncHttpxClientWrapper", DefaultHttpx2Client) + monkeypatch.setattr(base_client, "AsyncHttpxClientWrapper", DefaultAsyncHttpx2Client) diff --git a/tests/conftest.py b/tests/conftest.py index 74aebd8a25..8128f1515b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,9 +11,11 @@ import pytest from pytest_asyncio import is_async_test -from openai import OpenAI, AsyncOpenAI, DefaultAioHttpClient +from openai import OpenAI, AsyncOpenAI, DefaultHttpx2Client, DefaultAioHttpClient, DefaultAsyncHttpx2Client from openai._utils import is_dict +from ._httpx2_respx import enable_httpx2_respx + if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] @@ -30,10 +32,12 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) - # We skip tests that use both the aiohttp client and respx_mock as respx_mock - # doesn't support custom transports. + # RESPX cannot mock requests made by the aiohttp adapter. for item in items: - if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + if "respx_mock" not in item.fixturenames: + continue + + if "async_client" not in item.fixturenames: continue if not hasattr(item, "callspec"): @@ -45,19 +49,35 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +test_http_client = os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx") api_key = "My API Key" admin_api_key = "My Admin API Key" +@pytest.fixture(autouse=True) +def patch_httpx2_respx(request: FixtureRequest, monkeypatch: pytest.MonkeyPatch) -> None: + if test_http_client != "httpx2" or "respx_mock" not in request.fixturenames: + return + + router = request.getfixturevalue("respx_mock") + enable_httpx2_respx(router, monkeypatch, replace_sdk_defaults=request.path.name != "test_httpx2_base.py") + + @pytest.fixture(scope="session") def client(request: FixtureRequest) -> Iterator[OpenAI]: strict = getattr(request, "param", True) if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + http_client = DefaultHttpx2Client() if test_http_client == "httpx2" else None + with OpenAI( - base_url=base_url, api_key=api_key, admin_api_key=admin_api_key, _strict_response_validation=strict + base_url=base_url, + api_key=api_key, + admin_api_key=admin_api_key, + _strict_response_validation=strict, + http_client=http_client, ) as client: yield client @@ -85,6 +105,9 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenAI]: else: raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") + if http_client is None and test_http_client == "httpx2": + http_client = DefaultAsyncHttpx2Client() + async with AsyncOpenAI( base_url=base_url, api_key=api_key, diff --git a/tests/lib/test_bedrock.py b/tests/lib/test_bedrock.py index 14c91c0661..bf987b2fa9 100644 --- a/tests/lib/test_bedrock.py +++ b/tests/lib/test_bedrock.py @@ -14,6 +14,7 @@ from tests.utils import update_env from openai._types import Omit from openai.lib.bedrock import BedrockOpenAI, AsyncBedrockOpenAI +from tests._httpx2_respx import sync_http_client, async_http_client Client = Union[BedrockOpenAI, AsyncBedrockOpenAI] @@ -86,11 +87,11 @@ def __init__(self, access_key: str, secret_key: str, token: str | None = None) - def make_sync_client(**kwargs: Any) -> BedrockOpenAI: - return BedrockOpenAI(http_client=httpx.Client(trust_env=False), **kwargs) + return BedrockOpenAI(http_client=sync_http_client(trust_env=False), **kwargs) def make_async_client(**kwargs: Any) -> AsyncBedrockOpenAI: - return AsyncBedrockOpenAI(http_client=httpx.AsyncClient(trust_env=False), **kwargs) + return AsyncBedrockOpenAI(http_client=async_http_client(trust_env=False), **kwargs) def response_created_sse() -> str: @@ -333,7 +334,7 @@ def test_token_provider_refresh_sync(respx_mock: MockRouter) -> None: client = BedrockOpenAI( base_url="https://example.com/openai/v1", bedrock_token_provider=lambda: next(tokens), - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), max_retries=1, ) @@ -357,7 +358,7 @@ async def test_token_provider_refresh_async(respx_mock: MockRouter) -> None: client = AsyncBedrockOpenAI( base_url="https://example.com/openai/v1", bedrock_token_provider=lambda: next(tokens), - http_client=httpx.AsyncClient(trust_env=False), + http_client=async_http_client(trust_env=False), max_retries=1, ) @@ -380,7 +381,7 @@ def test_explicit_aws_credentials_override_ambient_bearer(respx_mock: MockRouter aws_access_key_id="access key", aws_secret_access_key="secret key", aws_session_token="session token", - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), ) client.responses.create(model="gpt-4o", input="hello") @@ -408,7 +409,7 @@ def test_aws_credentials_provider_refreshes_before_retries(respx_mock: MockRoute base_url="https://example.com/openai/v1", aws_region="us-east-1", aws_credentials_provider=lambda: next(credentials), - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), max_retries=1, ) @@ -425,7 +426,7 @@ def test_preserves_token_provider_across_with_options() -> None: client = BedrockOpenAI( base_url="https://example.com/openai/v1", bedrock_token_provider=lambda: "provider token", - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), ) copied_client = client.with_options(timeout=1) @@ -527,7 +528,7 @@ def test_explicit_aws_copy_override_wins_over_mutated_api_key() -> None: client = BedrockOpenAI( base_url="https://example.com/openai/v1", api_key="first token", - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), ) client.api_key = "second token" @@ -569,7 +570,7 @@ def test_legacy_state_repr_does_not_expose_credentials() -> None: aws_access_key_id="secret access key id", aws_secret_access_key="secret access key", aws_session_token="secret session token", - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), ) assert "secret" not in repr(client._bedrock_state) @@ -577,7 +578,7 @@ def test_legacy_state_repr_does_not_expose_credentials() -> None: bearer_client = BedrockOpenAI( base_url="https://example.com/openai/v1", api_key="secret bearer token", - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), ) assert "secret bearer token" not in repr(bearer_client._bedrock_runtime_signature) @@ -647,7 +648,7 @@ def test_preserves_aws_credentials_across_with_options() -> None: aws_region="us-east-1", aws_access_key_id="access key", aws_secret_access_key="secret key", - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), ) copied_client = client.with_options(timeout=1) @@ -941,7 +942,7 @@ def __init__( client = LegacyBedrockOpenAI( api_key="token", aws_region="us-east-1", - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), ) copied_client = client.with_options(timeout=1).with_options(aws_region="us-west-2") @@ -1121,7 +1122,7 @@ def test_refreshes_token_provider_for_admin_security_routes(respx_mock: MockRout client = BedrockOpenAI( base_url="https://example.com/openai/v1", bedrock_token_provider=lambda: next(tokens), - http_client=httpx.Client(trust_env=False), + http_client=sync_http_client(trust_env=False), max_retries=1, ) diff --git a/tests/test_client.py b/tests/test_client.py index 2d8955a58e..bdbc2ce26b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -117,10 +117,11 @@ async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] def _get_open_connections(client: OpenAI | AsyncOpenAI) -> int: transport = client._client._transport - assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) + if isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport): + return len(transport._pool._requests) - pool = transport._pool - return len(pool._requests) + assert type(transport).__module__ == "httpx2" + return len(cast(Any, transport)._pool._requests) class TestOpenAI: @@ -130,7 +131,7 @@ def test_raw_response(self, respx_mock: MockRouter, client: OpenAI) -> None: response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 - assert isinstance(response, httpx.Response) + assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx") assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) @@ -141,7 +142,7 @@ def test_raw_response_for_binary(self, respx_mock: MockRouter, client: OpenAI) - response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 - assert isinstance(response, httpx.Response) + assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx") assert response.json() == {"foo": "bar"} def test_copy(self, client: OpenAI) -> None: @@ -615,7 +616,7 @@ def test_default_query_option(self) -> None: def test_hardcoded_query_params_in_url(self, client: OpenAI) -> None: request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) - url = httpx.URL(request.url) + url = httpx.URL(str(request.url)) assert dict(url.params) == {"beta": "true"} request = client._build_request( @@ -625,7 +626,7 @@ def test_hardcoded_query_params_in_url(self, client: OpenAI) -> None: params={"limit": "10", "page": "abc"}, ) ) - url = httpx.URL(request.url) + url = httpx.URL(str(request.url)) assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} request = client._build_request( @@ -1393,7 +1394,7 @@ async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncOpe response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 - assert isinstance(response, httpx.Response) + assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx") assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) @@ -1404,7 +1405,7 @@ async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_clien response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 - assert isinstance(response, httpx.Response) + assert type(response).__module__ == os.environ.get("OPENAI_TEST_HTTP_CLIENT", "httpx") assert response.json() == {"foo": "bar"} def test_copy(self, async_client: AsyncOpenAI) -> None: @@ -1866,7 +1867,7 @@ async def test_default_query_option(self) -> None: async def test_hardcoded_query_params_in_url(self, async_client: AsyncOpenAI) -> None: request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) - url = httpx.URL(request.url) + url = httpx.URL(str(request.url)) assert dict(url.params) == {"beta": "true"} request = async_client._build_request( @@ -1876,7 +1877,7 @@ async def test_hardcoded_query_params_in_url(self, async_client: AsyncOpenAI) -> params={"limit": "10", "page": "abc"}, ) ) - url = httpx.URL(request.url) + url = httpx.URL(str(request.url)) assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} request = async_client._build_request( diff --git a/tests/test_httpx2.py b/tests/test_httpx2.py new file mode 100644 index 0000000000..3afd2d68d5 --- /dev/null +++ b/tests/test_httpx2.py @@ -0,0 +1,719 @@ +from __future__ import annotations + +import base64 +from typing import Any +from typing_extensions import override + +import httpx +import pytest + +import openai +from openai import ( + OpenAI, + AsyncOpenAI, + AzureOpenAI, + OpenAIError, + APIStatusError, + APITimeoutError, + AsyncAzureOpenAI, + APIConnectionError, +) +from openai._response import StreamAlreadyConsumed +from openai.providers import bedrock +from openai._constants import DEFAULT_TIMEOUT + +httpx2 = pytest.importorskip("httpx2") + + +@pytest.fixture(autouse=True) +def forbid_httpx_execution(monkeypatch: pytest.MonkeyPatch) -> None: + def forbidden(*_args: object, **_kwargs: object) -> None: + raise AssertionError("the experimental path unexpectedly executed an HTTPX client") + + monkeypatch.setattr(httpx.Client, "build_request", forbidden) + monkeypatch.setattr(httpx.Client, "send", forbidden) + monkeypatch.setattr(httpx.AsyncClient, "build_request", forbidden) + monkeypatch.setattr(httpx.AsyncClient, "send", forbidden) + + +def model_list(request: httpx.Request) -> httpx.Response: + return httpx2.Response(200, json={"object": "list", "data": []}, request=request) + + +def sse_response(request: httpx.Request) -> httpx.Response: + return httpx2.Response( + 200, + headers={"content-type": "text/event-stream"}, + content=( + b'data: {"type":"response.completed","response":{"id":"resp_test","object":"response",' + b'"created_at":0,"model":"gpt-4o","output":[],"parallel_tool_calls":false,' + b'"tool_choice":"auto","tools":[]}}\n\ndata: [DONE]\n\n' + ), + request=request, + ) + + +async def test_httpx2_helpers_supply_sdk_defaults_and_accept_native_proxy() -> None: + sync_client = openai.DefaultHttpx2Client(proxy="http://127.0.0.1:8080", trust_env=False) + async_client = openai.DefaultAsyncHttpx2Client(proxy="http://127.0.0.1:8080", trust_env=False) + try: + assert type(sync_client).__module__ == "httpx2" + assert type(async_client).__module__ == "httpx2" + assert sync_client.timeout.as_dict() == {"connect": 5.0, "read": 600, "write": 600, "pool": 600} + assert async_client.timeout.as_dict() == {"connect": 5.0, "read": 600, "write": 600, "pool": 600} + assert sync_client.follow_redirects + assert async_client.follow_redirects + finally: + sync_client.close() + await async_client.aclose() + + +def test_sync_helper_preserves_httpx2_family_for_parsed_raw_and_sse() -> None: + requests: list[httpx.Request] = [] + hooks: list[str] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return sse_response(request) if request.url.path.endswith("/responses") else model_list(request) + + def on_request(request: httpx.Request) -> None: + hooks.append(type(request).__module__) + + with OpenAI( + api_key="test", + base_url=httpx2.URL("https://example.test/v1"), + http_client=openai.DefaultHttpx2Client( + timeout=httpx.Timeout(30.0, read=10.0), + auth=httpx2.BasicAuth("fake-test-user", "fake-test-password"), + headers=[("x-repeated", "one"), ("x-repeated", "two")], + mounts={"https://example.test": httpx2.MockTransport(handler)}, + event_hooks={"request": [on_request]}, + trust_env=False, + ), + max_retries=0, + ) as client: + parsed = client.models.list(extra_query={"tag": ["one", "two"]}) + raw = client.models.with_raw_response.list() + stream = client.responses.create(model="gpt-4o", input="hello", stream=True) + events = list(stream) + multipart = client.post( + "/multipart", + files={"file": ("example.txt", b"body", "text/plain")}, + options={"headers": {"Content-Type": "multipart/form-data"}}, + cast_to=httpx.Response, + ) + + assert parsed.object == "list" + assert type(raw.http_response).__module__ == "httpx2" + assert type(raw.http_request).__module__ == "httpx2" + assert type(stream.response).__module__ == "httpx2" + assert [event.type for event in events] == ["response.completed"] + assert type(multipart).__module__ == "httpx2" + assert hooks == ["httpx2", "httpx2", "httpx2", "httpx2"] + assert all(type(request).__module__ == "httpx2" for request in requests) + assert ( + requests[0].headers["authorization"] + == f"Basic {base64.b64encode(b'fake-test-user:fake-test-password').decode()}" + ) + assert requests[0].headers.get_list("x-repeated") == ["one", "two"] + assert requests[0].url.params.get_list("tag[]") == ["one", "two"] + assert requests[0].extensions["timeout"] == {"connect": 30.0, "read": 10.0, "write": 30.0, "pool": 30.0} + assert requests[-1].headers["content-type"].startswith("multipart/form-data; boundary=") + + +async def test_async_helper_preserves_httpx2_family_for_parsed_raw_and_sse() -> None: + requests: list[httpx.Request] = [] + hooks: list[str] = [] + + async def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + return sse_response(request) if request.url.path.endswith("/responses") else model_list(request) + + async def on_request(request: httpx.Request) -> None: + hooks.append(type(request).__module__) + + async with AsyncOpenAI( + api_key="test", + base_url=httpx2.URL("https://example.test/v1"), + http_client=openai.DefaultAsyncHttpx2Client( + timeout=httpx.Timeout(30.0, read=10.0), + auth=httpx2.BasicAuth("fake-test-user", "fake-test-password"), + headers=[("x-repeated", "one"), ("x-repeated", "two")], + transport=httpx2.MockTransport(handler), + event_hooks={"request": [on_request]}, + trust_env=False, + ), + max_retries=0, + ) as client: + parsed = await client.models.list(extra_query={"tag": ["one", "two"]}) + raw = await client.models.with_raw_response.list() + stream = await client.responses.create(model="gpt-4o", input="hello", stream=True) + events = [event async for event in stream] + multipart = await client.post( + "/multipart", + files={"file": ("example.txt", b"body", "text/plain")}, + options={"headers": {"Content-Type": "multipart/form-data"}}, + cast_to=httpx.Response, + ) + + assert parsed.object == "list" + assert type(raw.http_response).__module__ == "httpx2" + assert type(raw.http_request).__module__ == "httpx2" + assert type(stream.response).__module__ == "httpx2" + assert [event.type for event in events] == ["response.completed"] + assert type(multipart).__module__ == "httpx2" + assert hooks == ["httpx2", "httpx2", "httpx2", "httpx2"] + assert all(type(request).__module__ == "httpx2" for request in requests) + assert ( + requests[0].headers["authorization"] + == f"Basic {base64.b64encode(b'fake-test-user:fake-test-password').decode()}" + ) + assert requests[0].headers.get_list("x-repeated") == ["one", "two"] + assert requests[0].url.params.get_list("tag[]") == ["one", "two"] + assert requests[0].extensions["timeout"] == {"connect": 30.0, "read": 10.0, "write": 30.0, "pool": 30.0} + assert requests[-1].headers["content-type"].startswith("multipart/form-data; boundary=") + + +def test_direct_sync_injection_and_module_configuration() -> None: + direct = httpx2.Client(transport=httpx2.MockTransport(model_list), trust_env=False) + with OpenAI(api_key="test", base_url="https://example.test/v1", http_client=direct, max_retries=0) as client: + assert client.timeout == DEFAULT_TIMEOUT + assert client.models.list().object == "list" + + openai.api_key = "test" + openai.base_url = httpx2.URL("https://example.test/v1") + openai.http_client = openai.DefaultHttpx2Client(transport=httpx2.MockTransport(model_list), trust_env=False) + try: + response = openai.models.with_raw_response.list() + finally: + openai._reset_client() + openai.http_client = None + + assert type(response.http_response).__module__ == "httpx2" + + +async def test_httpx2_urls_and_response_casts() -> None: + class ResponseSubclass(httpx2.Response): + pass + + def model(request: httpx.Request) -> httpx.Response: + return httpx2.Response( + 200, + request=request, + json={"id": "gpt-4o", "object": "model", "created": 1, "owned_by": "openai"}, + ) + + with OpenAI( + api_key="test", + base_url=httpx2.URL("https://example.test/v1"), + http_client=httpx2.Client(transport=httpx2.MockTransport(model), trust_env=False), + max_retries=0, + ) as client: + client.base_url = httpx2.URL("https://example.test/v1") + response = client.get("/models", cast_to=httpx2.Response) + legacy_raw = client.models.with_raw_response.retrieve("gpt-4o") + with client.models.with_streaming_response.retrieve("gpt-4o") as streaming_raw: + streaming_response = streaming_raw.parse(to=httpx2.Response) + + with pytest.raises(ValueError, match="Subclasses of HTTP response classes"): + client.get("/models", cast_to=ResponseSubclass) + + async def handler(request: httpx.Request) -> httpx.Response: + return model(request) + + async with AsyncOpenAI( + api_key="test", + base_url=httpx2.URL("https://example.test/v1"), + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False), + max_retries=0, + ) as async_client: + async_client.base_url = httpx2.URL("https://example.test/v1") + async_response = await async_client.get("/models", cast_to=httpx2.Response) + async_legacy_raw = await async_client.models.with_raw_response.retrieve("gpt-4o") + async with async_client.models.with_streaming_response.retrieve("gpt-4o") as async_streaming_raw: + async_streaming_response = await async_streaming_raw.parse(to=httpx2.Response) + + with pytest.raises(ValueError, match="Subclasses of HTTP response classes"): + await async_client.get("/models", cast_to=ResponseSubclass) + + assert isinstance(response, httpx2.Response) + assert isinstance(legacy_raw.parse(to=httpx2.Response), httpx2.Response) + assert isinstance(streaming_response, httpx2.Response) + assert isinstance(async_response, httpx2.Response) + assert isinstance(async_legacy_raw.parse(to=httpx2.Response), httpx2.Response) + assert isinstance(async_streaming_response, httpx2.Response) + + +async def test_httpx2_urls_work_for_bedrock_and_azure_realtime() -> None: + with OpenAI( + provider=bedrock( + region="us-east-1", + api_key="bedrock-token", + base_url=httpx2.URL("https://bedrock.test/openai/v1/responses"), + ), + http_client=httpx2.Client(transport=httpx2.MockTransport(model_list), trust_env=False), + max_retries=0, + ) as client: + assert client.models.list().object == "list" + + azure = AzureOpenAI( + api_key="test", + api_version="2024-02-01", + azure_endpoint="https://azure.test", + websocket_base_url=httpx2.URL("https://azure.test/openai/v1"), + http_client=httpx2.Client(transport=httpx2.MockTransport(model_list), trust_env=False), + ) + realtime_url, _ = azure._configure_realtime("gpt-4o", {}) + azure.close() + + async_azure = AsyncAzureOpenAI( + api_key="test", + api_version="2024-02-01", + azure_endpoint="https://azure.test", + websocket_base_url=httpx2.URL("https://azure.test/openai/v1"), + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(model_list), trust_env=False), + ) + async_realtime_url, _ = await async_azure._configure_realtime("gpt-4o", {}) + await async_azure.close() + + assert str(realtime_url).startswith("https://azure.test/openai/v1/realtime?") + assert str(async_realtime_url).startswith("https://azure.test/openai/v1/realtime?") + + +async def test_httpx2_urls_work_for_all_websocket_builders() -> None: + with OpenAI( + api_key="test", + websocket_base_url=httpx2.URL("wss://example.test/openai/v1"), + http_client=httpx2.Client(transport=httpx2.MockTransport(model_list), trust_env=False), + ) as client: + assert str(client.realtime.connect()._prepare_url()) == "wss://example.test/openai/v1/realtime" + assert str(client.responses.connect()._prepare_url()) == "wss://example.test/openai/v1/responses" + assert ( + str(client.beta.realtime.connect(model="gpt-4o")._prepare_url()) == "wss://example.test/openai/v1/realtime" + ) + assert str(client.beta.responses.connect()._prepare_url()) == "wss://example.test/openai/v1/responses" + + async with AsyncOpenAI( + api_key="test", + websocket_base_url=httpx2.URL("wss://example.test/openai/v1"), + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(model_list), trust_env=False), + ) as async_client: + assert str(async_client.realtime.connect()._prepare_url()) == "wss://example.test/openai/v1/realtime" + assert str(async_client.responses.connect()._prepare_url()) == "wss://example.test/openai/v1/responses" + assert ( + str(async_client.beta.realtime.connect(model="gpt-4o")._prepare_url()) + == "wss://example.test/openai/v1/realtime" + ) + assert str(async_client.beta.responses.connect()._prepare_url()) == "wss://example.test/openai/v1/responses" + + +async def test_httpx2_native_timeouts_set_numeric_read_timeout_header() -> None: + sync_requests: list[httpx.Request] = [] + async_requests: list[httpx.Request] = [] + + def sync_handler(request: httpx.Request) -> httpx.Response: + sync_requests.append(request) + return model_list(request) + + async def async_handler(request: httpx.Request) -> httpx.Response: + async_requests.append(request) + return model_list(request) + + with OpenAI( + api_key="test", + base_url="https://example.test/v1", + timeout=httpx2.Timeout(30.0, read=12.0), + http_client=httpx2.Client(transport=httpx2.MockTransport(sync_handler), trust_env=False), + max_retries=0, + ) as client: + client.models.list() + client.models.list(timeout=httpx2.Timeout(30.0, read=7.0)) + + async with AsyncOpenAI( + api_key="test", + base_url="https://example.test/v1", + timeout=httpx2.Timeout(30.0, read=13.0), + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(async_handler), trust_env=False), + max_retries=0, + ) as async_client: + await async_client.models.list() + await async_client.models.list(timeout=httpx2.Timeout(30.0, read=8.0)) + + assert [request.headers["x-stainless-read-timeout"] for request in sync_requests] == ["12.0", "7.0"] + assert [request.headers["x-stainless-read-timeout"] for request in async_requests] == ["13.0", "8.0"] + + +async def test_direct_async_injection() -> None: + async def handler(request: httpx.Request) -> httpx.Response: + return model_list(request) + + direct = httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False) + async with AsyncOpenAI( + api_key="test", base_url="https://example.test/v1", http_client=direct, max_retries=0 + ) as client: + assert client.timeout == DEFAULT_TIMEOUT + assert (await client.models.list()).object == "list" + + +@pytest.mark.parametrize("failure", ["timeout", "connection", "status"]) +def test_sync_retries_and_failure_families(failure: str, monkeypatch: pytest.MonkeyPatch) -> None: + requests: list[httpx.Request] = [] + + def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + if len(requests) > 1: + return model_list(request) + if failure == "timeout": + raise httpx2.ReadTimeout("timeout", request=request) + if failure == "connection": + raise httpx2.ConnectError("connection", request=request) + return httpx2.Response(500, json={"error": {"message": "bad", "type": "test"}}, request=request) + + client = OpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.Client(transport=httpx2.MockTransport(handler), trust_env=False), + max_retries=1, + ) + + def no_sleep(**_kwargs: Any) -> None: + return None + + monkeypatch.setattr(client, "_sleep_for_retry", no_sleep) + try: + assert client.models.list().object == "list" + finally: + client.close() + + assert len(requests) == 2 + assert all(type(request).__module__ == "httpx2" for request in requests) + + def always_fail(request: httpx.Request) -> httpx.Response: + if failure == "timeout": + raise httpx2.ReadTimeout("timeout", request=request) + if failure == "connection": + raise httpx2.ConnectError("connection", request=request) + return httpx2.Response(500, json={"error": {"message": "bad", "type": "test"}}, request=request) + + expected: type[Exception] + if failure == "timeout": + expected = APITimeoutError + elif failure == "connection": + expected = APIConnectionError + else: + expected = APIStatusError + + with OpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.Client(transport=httpx2.MockTransport(always_fail), trust_env=False), + max_retries=0, + ) as failing_client: + with pytest.raises(expected) as exc_info: + failing_client.models.list() + + error = exc_info.value + transport_value = getattr(error, "response", None) or getattr(error, "request", None) + assert type(transport_value).__module__ == "httpx2" + + +@pytest.mark.parametrize("failure", ["timeout", "connection", "status"]) +async def test_async_retries_and_failure_families(failure: str, monkeypatch: pytest.MonkeyPatch) -> None: + requests: list[httpx.Request] = [] + + async def handler(request: httpx.Request) -> httpx.Response: + requests.append(request) + if len(requests) > 1: + return model_list(request) + if failure == "timeout": + raise httpx2.ReadTimeout("timeout", request=request) + if failure == "connection": + raise httpx2.ConnectError("connection", request=request) + return httpx2.Response(500, json={"error": {"message": "bad", "type": "test"}}, request=request) + + client = AsyncOpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler), trust_env=False), + max_retries=1, + ) + + async def no_sleep(**_kwargs: Any) -> None: + return None + + monkeypatch.setattr(client, "_sleep_for_retry", no_sleep) + try: + assert (await client.models.list()).object == "list" + finally: + await client.close() + + assert len(requests) == 2 + assert all(type(request).__module__ == "httpx2" for request in requests) + + async def always_fail(request: httpx.Request) -> httpx.Response: + if failure == "timeout": + raise httpx2.ReadTimeout("timeout", request=request) + if failure == "connection": + raise httpx2.ConnectError("connection", request=request) + return httpx2.Response(500, json={"error": {"message": "bad", "type": "test"}}, request=request) + + expected: type[Exception] + if failure == "timeout": + expected = APITimeoutError + elif failure == "connection": + expected = APIConnectionError + else: + expected = APIStatusError + + async with AsyncOpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(always_fail), trust_env=False), + max_retries=0, + ) as failing_client: + with pytest.raises(expected) as exc_info: + await failing_client.models.list() + + error = exc_info.value + transport_value = getattr(error, "response", None) or getattr(error, "request", None) + assert type(transport_value).__module__ == "httpx2" + + +async def test_provider_auth_and_stream_consumed_families() -> None: + sync_requests: list[httpx.Request] = [] + async_requests: list[httpx.Request] = [] + + def sync_handler(request: httpx.Request) -> httpx.Response: + sync_requests.append(request) + return model_list(request) + + async def async_handler(request: httpx.Request) -> httpx.Response: + async_requests.append(request) + return model_list(request) + + with OpenAI( + provider=bedrock(region="us-east-1", api_key="bedrock-token", base_url="https://bedrock.test/openai/v1"), + http_client=openai.DefaultHttpx2Client(transport=httpx2.MockTransport(sync_handler), trust_env=False), + max_retries=0, + ) as sync_client: + assert sync_client.models.list().object == "list" + + async with AsyncOpenAI( + provider=bedrock(region="us-east-1", api_key="bedrock-token", base_url="https://bedrock.test/openai/v1"), + http_client=openai.DefaultAsyncHttpx2Client(transport=httpx2.MockTransport(async_handler), trust_env=False), + max_retries=0, + ) as async_client: + assert (await async_client.models.list()).object == "list" + + assert sync_requests[0].headers["authorization"] == "Bearer bedrock-token" + assert async_requests[0].headers["authorization"] == "Bearer bedrock-token" + + class SyncStream(httpx2.SyncByteStream): + def __iter__(self): + yield b'{"object":"list","data":[]}' + + class AsyncStream(httpx2.AsyncByteStream): + async def __aiter__(self): + yield b'{"object":"list","data":[]}' + + class FailingSyncStream(httpx2.SyncByteStream): + def __iter__(self): + yield b"partial" + raise httpx2.ReadTimeout("stream timeout") + + class FailingAsyncStream(httpx2.AsyncByteStream): + async def __aiter__(self): + yield b"partial" + raise httpx2.ReadTimeout("stream timeout") + + def sync_stream_handler(request: httpx.Request) -> httpx.Response: + return httpx2.Response(200, headers={"content-type": "application/json"}, stream=SyncStream(), request=request) + + async def async_stream_handler(request: httpx.Request) -> httpx.Response: + return httpx2.Response(200, headers={"content-type": "application/json"}, stream=AsyncStream(), request=request) + + def sync_failing_stream_handler(request: httpx.Request) -> httpx.Response: + return httpx2.Response( + 200, headers={"content-type": "application/json"}, stream=FailingSyncStream(), request=request + ) + + async def async_failing_stream_handler(request: httpx.Request) -> httpx.Response: + return httpx2.Response( + 200, headers={"content-type": "application/json"}, stream=FailingAsyncStream(), request=request + ) + + with OpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.Client(transport=httpx2.MockTransport(sync_stream_handler), trust_env=False), + max_retries=0, + ) as sync_client: + with sync_client.models.with_streaming_response.list() as response: + assert b"".join(response.iter_bytes()) == b'{"object":"list","data":[]}' + with pytest.raises(StreamAlreadyConsumed): + response.read() + + with OpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.Client(transport=httpx2.MockTransport(sync_failing_stream_handler), trust_env=False), + max_retries=0, + ) as sync_client: + with sync_client.models.with_streaming_response.list() as response: + with pytest.raises(httpx2.ReadTimeout, match="stream timeout"): + list(response.iter_bytes()) + + async with AsyncOpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(async_stream_handler), trust_env=False), + max_retries=0, + ) as async_client: + async with async_client.models.with_streaming_response.list() as response: + assert b"".join([chunk async for chunk in response.iter_bytes()]) == b'{"object":"list","data":[]}' + with pytest.raises(StreamAlreadyConsumed): + await response.read() + + async with AsyncOpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(async_failing_stream_handler), trust_env=False), + max_retries=0, + ) as async_client: + async with async_client.models.with_streaming_response.list() as response: + with pytest.raises(httpx2.ReadTimeout, match="stream timeout"): + [chunk async for chunk in response.iter_bytes()] + + +@pytest.mark.filterwarnings("ignore:The Assistants API is deprecated in favor of the Responses API:DeprecationWarning") +async def test_assistant_stream_timeout_callbacks_preserve_httpx2_family() -> None: + class SyncHandler(openai.AssistantEventHandler): + def __init__(self) -> None: + super().__init__() + self.timed_out = False + self.exception: Exception | None = None + + @override + def on_timeout(self) -> None: + self.timed_out = True + + @override + def on_exception(self, exception: Exception) -> None: + self.exception = exception + + class AsyncHandler(openai.AsyncAssistantEventHandler): + def __init__(self) -> None: + super().__init__() + self.timed_out = False + self.exception: Exception | None = None + + @override + async def on_timeout(self) -> None: + self.timed_out = True + + @override + async def on_exception(self, exception: Exception) -> None: + self.exception = exception + + class FailingSyncStream(httpx2.SyncByteStream): + def __iter__(self): + yield b"partial" + raise httpx2.ReadTimeout("assistant stream timeout") + + class FailingAsyncStream(httpx2.AsyncByteStream): + async def __aiter__(self): + yield b"partial" + raise httpx2.ReadTimeout("assistant stream timeout") + + def sync_response(request: httpx.Request) -> httpx.Response: + return httpx2.Response( + 200, headers={"content-type": "text/event-stream"}, stream=FailingSyncStream(), request=request + ) + + async def async_response(request: httpx.Request) -> httpx.Response: + return httpx2.Response( + 200, headers={"content-type": "text/event-stream"}, stream=FailingAsyncStream(), request=request + ) + + sync_handler = SyncHandler() + with OpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=openai.DefaultHttpx2Client(transport=httpx2.MockTransport(sync_response), trust_env=False), + max_retries=0, + ) as sync_client: + with sync_client.beta.threads.runs.stream( # pyright: ignore[reportDeprecated] + assistant_id="asst_test", thread_id="thread_test", event_handler=sync_handler + ) as stream: + with pytest.raises(httpx2.ReadTimeout, match="assistant stream timeout"): + stream.until_done() + + assert sync_handler.timed_out + assert isinstance(sync_handler.exception, httpx2.ReadTimeout) + + async_handler = AsyncHandler() + async with AsyncOpenAI( + api_key="test", + base_url="https://example.test/v1", + http_client=openai.DefaultAsyncHttpx2Client(transport=httpx2.MockTransport(async_response), trust_env=False), + max_retries=0, + ) as async_client: + async with async_client.beta.threads.runs.stream( # pyright: ignore[reportDeprecated] + assistant_id="asst_test", thread_id="thread_test", event_handler=async_handler + ) as async_stream: + with pytest.raises(httpx2.ReadTimeout, match="assistant stream timeout"): + await async_stream.until_done() + + assert async_handler.timed_out + assert isinstance(async_handler.exception, httpx2.ReadTimeout) + + +async def test_sigv4_provider_preserves_httpx2_family_and_rejects_one_shot_bodies() -> None: + pytest.importorskip("botocore") + + sync_requests: list[httpx.Request] = [] + async_requests: list[httpx.Request] = [] + + def sync_handler(request: httpx.Request) -> httpx.Response: + sync_requests.append(request) + return model_list(request) + + async def async_handler(request: httpx.Request) -> httpx.Response: + async_requests.append(request) + return model_list(request) + + provider = bedrock( + region="us-east-1", + access_key_id="fixture-access-key", + secret_access_key="fixture-secret-key", + session_token="fixture-session-token", + base_url="https://bedrock-mantle.us-east-1.api.aws/openai/v1", + ) + + with OpenAI( + provider=provider, + http_client=openai.DefaultHttpx2Client(transport=httpx2.MockTransport(sync_handler), trust_env=False), + max_retries=0, + ) as sync_client: + sync_client.post("/responses", content=b"body", cast_to=httpx.Response) + with pytest.raises(OpenAIError, match="requires a replayable request body"): + sync_client.post("/responses", content=iter([b"body"]), cast_to=httpx.Response) + + async def body(): + yield b"body" + + async with AsyncOpenAI( + provider=provider, + http_client=openai.DefaultAsyncHttpx2Client(transport=httpx2.MockTransport(async_handler), trust_env=False), + max_retries=0, + ) as async_client: + await async_client.post("/responses", content=b"body", cast_to=httpx.Response) + with pytest.raises(OpenAIError, match="requires a replayable request body"): + await async_client.post("/responses", content=body(), cast_to=httpx.Response) + + assert len(sync_requests) == 1 + assert len(async_requests) == 1 + assert "Credential=fixture-access-key/" in sync_requests[0].headers["authorization"] + assert "Credential=fixture-access-key/" in async_requests[0].headers["authorization"] + assert sync_requests[0].headers["x-amz-security-token"] == "fixture-session-token" + assert async_requests[0].headers["x-amz-security-token"] == "fixture-session-token" diff --git a/tests/test_httpx2_base.py b/tests/test_httpx2_base.py new file mode 100644 index 0000000000..eb81c69a98 --- /dev/null +++ b/tests/test_httpx2_base.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +import sys +import asyncio +import warnings +import threading +import subprocess +import importlib.util +from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler +from typing_extensions import override + +import httpx +import respx +import pytest + +import openai +from openai import OpenAI, AsyncOpenAI, _httpx2 as httpx2_helpers + + +def test_base_import_does_not_load_httpx2() -> None: + subprocess.run([sys.executable, "-c", "import sys; import openai; assert 'httpx2' not in sys.modules"], check=True) + + +def test_missing_httpx2_extra_is_actionable(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(httpx2_helpers.sys, "version_info", (3, 10)) + + def missing_httpx2(name: str) -> object: + raise ImportError(name) + + monkeypatch.setattr(httpx2_helpers.importlib, "import_module", missing_httpx2) + + for helper in (openai.DefaultHttpx2Client, openai.DefaultAsyncHttpx2Client): + with pytest.raises(RuntimeError, match=r"install the httpx2 extra: pip install 'openai\[httpx2\]'"): + helper() + + +def test_python39_httpx2_error_is_actionable(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(httpx2_helpers.sys, "version_info", (3, 9)) + + for helper in (openai.DefaultHttpx2Client, openai.DefaultAsyncHttpx2Client): + with pytest.raises(RuntimeError, match=r"HTTPX2 requires Python 3\.10 or later.*openai\[httpx2\]"): + helper() + + +@pytest.mark.respx(base_url="https://example.test/v1") +def test_default_httpx_family_and_respx_are_unchanged(respx_mock: respx.MockRouter) -> None: + route = respx_mock.get("/models").mock(return_value=httpx.Response(200, json={"object": "list", "data": []})) + + with warnings.catch_warnings(record=True) as captured: + warnings.simplefilter("always") + with OpenAI(api_key="test", base_url="https://example.test/v1") as client: + response = client.models.with_raw_response.list() + + assert route.called + assert captured == [] + assert isinstance(response.http_response, httpx.Response) + assert isinstance(response.http_request, httpx.Request) + + +async def test_existing_httpx_helpers_and_injected_clients_are_unchanged() -> None: + class SyncHttpxClient(openai.DefaultHttpxClient): + pass + + class AsyncHttpxClient(openai.DefaultAsyncHttpxClient): + pass + + def handler(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, json={"object": "list", "data": []}, request=request) + + with SyncHttpxClient(transport=httpx.MockTransport(handler), trust_env=False) as http_client: + with OpenAI(api_key="test", base_url="https://example.test/v1", http_client=http_client) as client: + response = client.models.with_raw_response.list() + assert isinstance(response.http_response, httpx.Response) + + async with AsyncHttpxClient(transport=httpx.MockTransport(handler), trust_env=False) as http_client: + async with AsyncOpenAI(api_key="test", base_url="https://example.test/v1", http_client=http_client) as client: + response = await client.models.with_raw_response.list() + assert isinstance(response.http_response, httpx.Response) + + +async def test_existing_aiohttp_adapter_is_unchanged_when_installed() -> None: + if importlib.util.find_spec("httpx_aiohttp") is None: + pytest.skip("the aiohttp extra is not installed") + + class ModelsHandler(BaseHTTPRequestHandler): + def do_GET(self) -> None: + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write(b'{"object":"list","data":[]}') + + @override + def log_message(self, format: str, *_args: object) -> None: # noqa: A002 + return None + + server = ThreadingHTTPServer(("127.0.0.1", 0), ModelsHandler) + thread = threading.Thread(target=server.serve_forever) + thread.start() + try: + async with AsyncOpenAI( + api_key="test", + base_url=f"http://127.0.0.1:{server.server_port}/v1", + http_client=openai.DefaultAioHttpClient(), + max_retries=0, + ) as client: + response = await client.models.with_raw_response.list() + finally: + await asyncio.to_thread(server.shutdown) + thread.join() + server.server_close() + + assert isinstance(response.http_response, httpx.Response) diff --git a/tests/test_httpx2_respx.py b/tests/test_httpx2_respx.py new file mode 100644 index 0000000000..37c42d9628 --- /dev/null +++ b/tests/test_httpx2_respx.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import os + +import httpx +import pytest +from respx import MockRouter + +from openai import OpenAI, AsyncOpenAI, APITimeoutError + +httpx2 = pytest.importorskip("httpx2") +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +pytestmark = pytest.mark.skipif( + os.environ.get("OPENAI_TEST_HTTP_CLIENT") != "httpx2", reason="requires the HTTPX2 test lane" +) + + +@pytest.mark.respx(base_url=base_url) +def test_respx_bridge_preserves_native_sync_family_and_request_content(client: OpenAI, respx_mock: MockRouter) -> None: + def mirror(request: httpx.Request) -> httpx.Response: + assert request.url.path == "/upload" + assert request.headers["x-test"] == "sync" + return httpx.Response(200, content=request.content) + + respx_mock.post("/upload").mock(side_effect=mirror) + + response = client.post( + "/upload", content=b"sync body", options={"headers": {"x-test": "sync"}}, cast_to=httpx2.Response + ) + + assert isinstance(response, httpx2.Response) + assert isinstance(response.request, httpx2.Request) + assert response.content == b"sync body" + assert len(respx_mock.calls) == 1 + + +@pytest.mark.respx(base_url=base_url) +async def test_respx_bridge_preserves_native_async_family_and_request_content( + async_client: AsyncOpenAI, respx_mock: MockRouter +) -> None: + async def mirror(request: httpx.Request) -> httpx.Response: + assert request.url.path == "/upload" + assert request.headers["x-test"] == "async" + return httpx.Response(200, content=await request.aread()) + + respx_mock.post("/upload").mock(side_effect=mirror) + + response = await async_client.post( + "/upload", content=b"async body", options={"headers": {"x-test": "async"}}, cast_to=httpx2.Response + ) + + assert isinstance(response, httpx2.Response) + assert isinstance(response.request, httpx2.Request) + assert response.content == b"async body" + assert len(respx_mock.calls) == 1 + + +@pytest.mark.respx(base_url=base_url) +def test_respx_bridge_maps_timeout_to_native_family(client: OpenAI, respx_mock: MockRouter) -> None: + respx_mock.get("/models").mock(side_effect=httpx.ReadTimeout("mock timeout")) + + with pytest.raises(APITimeoutError) as exc_info: + client.with_options(max_retries=0).models.list() + + assert isinstance(exc_info.value.__cause__, httpx2.ReadTimeout) diff --git a/tests/test_httpx2_workload.py b/tests/test_httpx2_workload.py new file mode 100644 index 0000000000..14bb997248 --- /dev/null +++ b/tests/test_httpx2_workload.py @@ -0,0 +1,305 @@ +from __future__ import annotations + +import json +from typing import Any, NoReturn, cast + +import httpx +import respx +import pytest +from respx.models import Call + +import openai._base_client as base_client +import openai.auth._workload as workload +from openai import ( + OpenAI, + OAuthError, + AsyncOpenAI, + APITimeoutError, + APIConnectionError, + DefaultHttpx2Client, + DefaultAsyncHttpx2Client, +) +from openai.auth import WorkloadIdentity + +httpx2 = pytest.importorskip("httpx2") + + +def workload_identity(get_token: Any = lambda: "subject-token") -> WorkloadIdentity: + return { + "identity_provider_id": "idp_123", + "service_account_id": "sa_123", + "provider": {"get_token": get_token, "token_type": "jwt"}, + } + + +def exchange_payload(access_token: str) -> dict[str, object]: + return {"access_token": access_token, "expires_in": 3600} + + +def forbidden_httpx_send(*_args: Any, **_kwargs: Any) -> NoReturn: + pytest.fail("HTTPX unexpectedly sent a request") + + +def test_sync_httpx2_workload_exchange_is_native_and_cached(monkeypatch: pytest.MonkeyPatch) -> None: + exchange_requests: list[Any] = [] + api_requests: list[Any] = [] + provider_calls = 0 + + def get_token() -> str: + nonlocal provider_calls + provider_calls += 1 + return "subject-token" + + def exchange_handler(request: Any) -> Any: + exchange_requests.append(request) + return httpx2.Response(200, request=request, json=exchange_payload("access-token")) + + def exchange_client(**kwargs: Any) -> Any: + assert kwargs == {"follow_redirects": False} + return httpx2.Client(transport=httpx2.MockTransport(exchange_handler), trust_env=False) + + def api_handler(request: Any) -> Any: + api_requests.append(request) + return httpx2.Response(200, request=request, json={"object": "list", "data": []}) + + monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client) + monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send) + + with OpenAI( + workload_identity=workload_identity(get_token), + base_url="https://api.example.test/v1", + http_client=httpx2.Client(transport=httpx2.MockTransport(api_handler), trust_env=False), + max_retries=0, + ) as client: + assert client.models.list().object == "list" + assert client.models.list().object == "list" + + assert provider_calls == 1 + assert len(exchange_requests) == 1 + assert len(api_requests) == 2 + assert isinstance(exchange_requests[0], httpx2.Request) + assert str(exchange_requests[0].url) == "https://auth.openai.com/oauth/token" + assert json.loads(exchange_requests[0].content) == { + "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", + "subject_token": "subject-token", + "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", + "identity_provider_id": "idp_123", + "service_account_id": "sa_123", + } + assert all(isinstance(request, httpx2.Request) for request in api_requests) + assert [request.headers["authorization"] for request in api_requests] == ["Bearer access-token"] * 2 + + +async def test_async_httpx2_workload_401_reexchanges_with_sync_native_client(monkeypatch: pytest.MonkeyPatch) -> None: + exchange_requests: list[Any] = [] + api_requests: list[Any] = [] + api_authorizations: list[str] = [] + tokens = iter(["access-token-1", "access-token-2"]) + + def exchange_handler(request: Any) -> Any: + exchange_requests.append(request) + return httpx2.Response(200, request=request, json=exchange_payload(next(tokens))) + + def exchange_client(**kwargs: Any) -> Any: + assert kwargs == {"follow_redirects": False} + return httpx2.Client(transport=httpx2.MockTransport(exchange_handler), trust_env=False) + + async def api_handler(request: Any) -> Any: + api_requests.append(request) + api_authorizations.append(request.headers["authorization"]) + status_code = 401 if len(api_requests) == 1 else 200 + return httpx2.Response(status_code, request=request, json={"object": "list", "data": []}) + + monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client) + monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send) + monkeypatch.setattr(httpx.AsyncClient, "send", forbidden_httpx_send) + + async with AsyncOpenAI( + workload_identity=workload_identity(), + base_url="https://api.example.test/v1", + http_client=httpx2.AsyncClient(transport=httpx2.MockTransport(api_handler), trust_env=False), + max_retries=0, + ) as client: + assert (await client.models.list()).object == "list" + + assert len(exchange_requests) == 2 + assert all(isinstance(request, httpx2.Request) for request in exchange_requests) + assert len(api_requests) == 2 + assert all(isinstance(request, httpx2.Request) for request in api_requests) + assert api_authorizations == ["Bearer access-token-1", "Bearer access-token-2"] + + +def test_sync_httpx2_default_workload_exchange_is_native(monkeypatch: pytest.MonkeyPatch) -> None: + exchange_requests: list[Any] = [] + api_requests: list[Any] = [] + + def exchange_handler(request: Any) -> Any: + exchange_requests.append(request) + return httpx2.Response(200, request=request, json=exchange_payload("access-token")) + + def exchange_client(**kwargs: Any) -> Any: + assert kwargs == {"follow_redirects": False} + return httpx2.Client(transport=httpx2.MockTransport(exchange_handler), trust_env=False) + + def api_handler(request: Any) -> Any: + api_requests.append(request) + return httpx2.Response(200, request=request, json={"object": "list", "data": []}) + + def default_client(**kwargs: Any) -> Any: + return DefaultHttpx2Client(transport=httpx2.MockTransport(api_handler), trust_env=False, **kwargs) + + monkeypatch.setattr(base_client, "SyncHttpxClientWrapper", default_client) + monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client) + monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send) + + with OpenAI( + workload_identity=workload_identity(), + base_url="https://api.example.test/v1", + max_retries=0, + ) as client: + assert isinstance(client._client, httpx2.Client) + assert client.models.list().object == "list" + + assert len(exchange_requests) == 1 + assert len(api_requests) == 1 + assert isinstance(exchange_requests[0], httpx2.Request) + assert isinstance(api_requests[0], httpx2.Request) + assert api_requests[0].headers["authorization"] == "Bearer access-token" + + +async def test_async_httpx2_default_workload_exchange_is_native(monkeypatch: pytest.MonkeyPatch) -> None: + exchange_requests: list[Any] = [] + api_requests: list[Any] = [] + + def exchange_handler(request: Any) -> Any: + exchange_requests.append(request) + return httpx2.Response(200, request=request, json=exchange_payload("access-token")) + + def exchange_client(**kwargs: Any) -> Any: + assert kwargs == {"follow_redirects": False} + return httpx2.Client(transport=httpx2.MockTransport(exchange_handler), trust_env=False) + + async def api_handler(request: Any) -> Any: + api_requests.append(request) + return httpx2.Response(200, request=request, json={"object": "list", "data": []}) + + def default_client(**kwargs: Any) -> Any: + return DefaultAsyncHttpx2Client(transport=httpx2.MockTransport(api_handler), trust_env=False, **kwargs) + + monkeypatch.setattr(base_client, "AsyncHttpxClientWrapper", default_client) + monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client) + monkeypatch.setattr(httpx.Client, "send", forbidden_httpx_send) + monkeypatch.setattr(httpx.AsyncClient, "send", forbidden_httpx_send) + + async with AsyncOpenAI( + workload_identity=workload_identity(), + base_url="https://api.example.test/v1", + max_retries=0, + ) as client: + assert isinstance(client._client, httpx2.AsyncClient) + assert (await client.models.list()).object == "list" + + assert len(exchange_requests) == 1 + assert len(api_requests) == 1 + assert isinstance(exchange_requests[0], httpx2.Request) + assert isinstance(api_requests[0], httpx2.Request) + assert api_requests[0].headers["authorization"] == "Bearer access-token" + + +def test_httpx2_workload_oauth_error_preserves_native_response(monkeypatch: pytest.MonkeyPatch) -> None: + api_calls = 0 + + def exchange_handler(request: Any) -> Any: + return httpx2.Response( + 401, + request=request, + json={"error": "invalid_grant", "error_description": "invalid workload identity"}, + ) + + def exchange_client(**kwargs: Any) -> Any: + assert kwargs == {"follow_redirects": False} + return httpx2.Client(transport=httpx2.MockTransport(exchange_handler), trust_env=False) + + def api_handler(request: Any) -> Any: + nonlocal api_calls + api_calls += 1 + return httpx2.Response(200, request=request, json={"object": "list", "data": []}) + + monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client) + + with OpenAI( + workload_identity=workload_identity(), + base_url="https://api.example.test/v1", + http_client=httpx2.Client(transport=httpx2.MockTransport(api_handler), trust_env=False), + max_retries=0, + ) as client: + with pytest.raises(OAuthError) as exc_info: + client.models.list() + + assert exc_info.value.message == "invalid workload identity" + assert isinstance(exc_info.value.response, httpx2.Response) + assert isinstance(exc_info.value.request, httpx2.Request) + assert api_calls == 0 + + +@pytest.mark.parametrize( + ("failure", "expected"), + [("timeout", APITimeoutError), ("connection", APIConnectionError)], +) +def test_httpx2_workload_exchange_transport_failure( + failure: str, expected: type[Exception], monkeypatch: pytest.MonkeyPatch +) -> None: + def exchange_handler(request: Any) -> Any: + if failure == "timeout": + raise httpx2.ReadTimeout("exchange timeout", request=request) + raise httpx2.ConnectError("exchange unavailable", request=request) + + def exchange_client(**kwargs: Any) -> Any: + assert kwargs == {"follow_redirects": False} + return httpx2.Client(transport=httpx2.MockTransport(exchange_handler), trust_env=False) + + def api_handler(request: Any) -> Any: + return httpx2.Response(200, request=request) + + monkeypatch.setattr(workload, "DefaultHttpx2Client", exchange_client) + + with OpenAI( + workload_identity=workload_identity(), + base_url="https://api.example.test/v1", + http_client=httpx2.Client(transport=httpx2.MockTransport(api_handler)), + max_retries=0, + ) as client: + with pytest.raises(expected) as exc_info: + client.models.list() + + assert type(exc_info.value.__cause__).__module__ == "httpx2" + + +def test_httpx_workload_exchange_stays_httpx_when_httpx2_is_installed(monkeypatch: pytest.MonkeyPatch) -> None: + api_requests: list[httpx.Request] = [] + + def forbidden_httpx2(**_kwargs: Any) -> Any: + pytest.fail("HTTPX2 unexpectedly created a workload exchange client") + + def api_handler(request: httpx.Request) -> httpx.Response: + api_requests.append(request) + return httpx.Response(200, request=request, json={"object": "list", "data": []}) + + monkeypatch.setattr(workload, "DefaultHttpx2Client", forbidden_httpx2) + + with respx.mock(assert_all_mocked=False) as router: + exchange = router.post("https://auth.openai.com/oauth/token").mock( + return_value=httpx.Response(200, json=exchange_payload("access-token")) + ) + with OpenAI( + workload_identity=workload_identity(), + base_url="https://api.example.test/v1", + http_client=httpx.Client(transport=httpx.MockTransport(api_handler), trust_env=False), + max_retries=0, + ) as client: + assert client.models.list().object == "list" + + assert exchange.call_count == 1 + assert len(api_requests) == 1 + assert isinstance(cast(Call, exchange.calls[0]).request, httpx.Request) + assert isinstance(api_requests[0], httpx.Request) diff --git a/uv.lock b/uv.lock index d3c4fe3ba4..f5d56c9c6e 100644 --- a/uv.lock +++ b/uv.lock @@ -571,6 +571,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] +[[package]] +name = "httpcore2" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11", marker = "python_full_version >= '3.10'" }, + { name = "truststore", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/fe/6a3f9f1a8bb8733326140737446aaf72fddb8b54b8f202302f5c84960613/httpcore2-2.7.0.tar.gz", hash = "sha256:6dc0fedf329a52a990930a5579edfebaea81118ea700ea0dd7de2b5e5be49efc", size = 65593, upload-time = "2026-07-14T20:40:01.111Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/6c/62e2e279e63fc4f7a5ee841ef13175a8bbc613f258e9dcc186e9de803a42/httpcore2-2.7.0-py3-none-any.whl", hash = "sha256:1452f589fe23f55b44546cd884294c41a29330af902bc0b71a761fd52d18f92b", size = 81506, upload-time = "2026-07-14T20:39:58.053Z" }, +] + [[package]] name = "httpx" version = "0.28.1" @@ -600,6 +613,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/8d/85c9701e9af72ca132a1783e2a54364a90c6da832304416a30fc11196ab2/httpx_aiohttp-0.1.12-py3-none-any.whl", hash = "sha256:5b0eac39a7f360fa7867a60bcb46bb1024eada9c01cbfecdb54dc1edb3fb7141", size = 6367, upload-time = "2025-12-12T10:12:14.018Z" }, ] +[[package]] +name = "httpx2" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", version = "4.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "httpcore2", marker = "python_full_version >= '3.10'" }, + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "truststore", marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/4a/129b2e21b90ac2985d3928d96792bccc39bc6dfe796c5eee2d8ec06d4105/httpx2-2.7.0.tar.gz", hash = "sha256:8b30709aed5c8465b0dd3b95c09ce301c8f79e7e7a2d00ab0af551e0d0375b07", size = 94487, upload-time = "2026-07-14T20:40:02.318Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/b8/c341bba6411bdfda786020343c47a75ef472f6085caf82391b142b1a3ad9/httpx2-2.7.0-py3-none-any.whl", hash = "sha256:ed2a2719c696789e09493bd8e2bec3d8bd925cc6e26b68389ec25ade132f7bf4", size = 90234, upload-time = "2026-07-14T20:39:59.531Z" }, +] + [[package]] name = "idna" version = "3.18" @@ -1132,6 +1161,11 @@ datalib = [ { name = "pandas-stubs", version = "2.3.3.260113", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, { name = "pandas-stubs", version = "3.0.3.260530", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] +httpx2 = [ + { name = "anyio", version = "4.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "httpx", marker = "python_full_version >= '3.10'" }, + { name = "httpx2", marker = "python_full_version >= '3.10'" }, +] realtime = [ { name = "websockets" }, ] @@ -1146,11 +1180,14 @@ voice-helpers = [ requires-dist = [ { name = "aiohttp", marker = "python_full_version >= '3.10' and extra == 'aiohttp'", specifier = ">=3.14.1" }, { name = "anyio", specifier = ">=3.5.0,<5" }, + { name = "anyio", marker = "python_full_version >= '3.10' and extra == 'httpx2'", specifier = ">=4.10.0,<5" }, { name = "botocore", marker = "python_full_version >= '3.10' and extra == 'bedrock'", specifier = ">=1.40.0,<2" }, { name = "botocore", marker = "python_full_version < '3.10' and extra == 'bedrock'", specifier = ">=1.40.0,<1.43" }, { name = "distro", specifier = ">=1.7.0,<2" }, { name = "httpx", specifier = ">=0.23.0,<1" }, + { name = "httpx", marker = "python_full_version >= '3.10' and extra == 'httpx2'", specifier = ">=0.25.1,<1" }, { name = "httpx-aiohttp", marker = "python_full_version >= '3.10' and extra == 'aiohttp'", specifier = ">=0.1.9" }, + { name = "httpx2", marker = "python_full_version >= '3.10' and extra == 'httpx2'", specifier = ">=2.7.0,<3" }, { name = "jiter", specifier = ">=0.10.0,<1" }, { name = "numpy", marker = "extra == 'datalib'", specifier = ">=1" }, { name = "numpy", marker = "extra == 'voice-helpers'", specifier = ">=2.0.2" }, @@ -1163,7 +1200,7 @@ requires-dist = [ { name = "typing-extensions", specifier = ">=4.14,<5" }, { name = "websockets", marker = "extra == 'realtime'", specifier = ">=13,<16" }, ] -provides-extras = ["aiohttp", "realtime", "datalib", "voice-helpers", "bedrock"] +provides-extras = ["aiohttp", "httpx2", "realtime", "datalib", "voice-helpers", "bedrock"] [[package]] name = "pandas" @@ -1728,6 +1765,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d8/8e/bb97bb0c71802080bfc8952937d174e49cfc50de5c951dd47b2496f0dcdb/tqdm-4.68.3-py3-none-any.whl", hash = "sha256:39832cc2def2789a6f29df83f172db7416cea70052c0907a57801c5f2fdccb03", size = 78337, upload-time = "2026-06-17T07:36:50.132Z" }, ] +[[package]] +name = "truststore" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" }, +] + [[package]] name = "types-pytz" version = "2025.2.0.20251108" From e67afa88433dad9f97e733ae8f2ee6e9c240bc51 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 20:57:58 +0000 Subject: [PATCH 405/408] release: 2.47.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7336785395..3ab3b9e3de 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.46.0" + ".": "2.47.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5747875ef1..90e3d203a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 2.47.0 (2026-07-21) + +Full Changelog: [v2.46.0...v2.47.0](https://github.com/openai/openai-python/compare/v2.46.0...v2.47.0) + +### Features + +* **client:** Add experimental runtime support for HTTPX2 clients ([#3524](https://github.com/openai/openai-python/issues/3524)) ([317260c](https://github.com/openai/openai-python/commit/317260cd16395b2338bcdaacd7daa0b179c90105)) +* **stlc:** configurable CI runner and private-production-repo support in workflow templates ([4303e97](https://github.com/openai/openai-python/commit/4303e974f08394789c6075b6745357b2f1c36e21)) + + +### Bug Fixes + +* **deps:** require patched aiohttp on Python 3.10+ ([#3515](https://github.com/openai/openai-python/issues/3515)) ([d4dceb2](https://github.com/openai/openai-python/commit/d4dceb221b9a92c55c232d5b330ae89beb539415)) + ## 2.46.0 (2026-07-17) Full Changelog: [v2.45.0...v2.46.0](https://github.com/openai/openai-python/compare/v2.45.0...v2.46.0) diff --git a/pyproject.toml b/pyproject.toml index 7b8fae090d..c99f41b56f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.46.0" +version = "2.47.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index a827c30678..33a4084e62 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.46.0" # x-release-please-version +__version__ = "2.47.0" # x-release-please-version From 00709f2ae951ce79705467e92d71a926a46b82f6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 20:45:29 +0000 Subject: [PATCH 406/408] feat(api): accept `None` for prompt_cache_key/safety_identifier --- .stats.yml | 4 +- .../resources/beta/responses/responses.py | 40 +++++------ .../resources/chat/completions/completions.py | 66 ++++++++++--------- src/openai/resources/responses/responses.py | 64 +++++++++--------- src/openai/types/beta/beta_response.py | 5 +- src/openai/types/beta/beta_response_error.py | 1 + .../types/beta/beta_response_text_config.py | 2 +- .../beta/beta_response_text_config_param.py | 2 +- .../types/beta/beta_responses_client_event.py | 5 +- .../beta/beta_responses_client_event_param.py | 9 ++- .../types/beta/response_create_params.py | 9 ++- .../responses/input_token_count_params.py | 7 +- .../types/chat/completion_create_params.py | 6 +- .../responses/input_token_count_params.py | 2 +- .../types/responses/response_create_params.py | 4 +- src/openai/types/responses/response_error.py | 1 + .../types/responses/response_text_config.py | 2 +- .../responses/response_text_config_param.py | 2 +- .../responses/responses_client_event_param.py | 4 +- src/openai/types/shared/reasoning.py | 5 +- src/openai/types/shared_params/reasoning.py | 5 +- 21 files changed, 137 insertions(+), 108 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7f13b9ff35..39bf33e59e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 272 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-8ed7dcb0d163e7f5205dbe172d6ad5b7ce367b23c20629830e39a0aee920a029.yml -openapi_spec_hash: 011cdae0c67909fb80a4a5eb54ca5bca +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5555d8e0f800f68d951749454d94fa8b899fc3722550657578b1d8a9fd3322e8.yml +openapi_spec_hash: 754a2e573085f55d60f02dbc3a5ef2b1 config_hash: 896f0f71bc7d1ee09435813cdef97523 diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py index 4ec4723d20..ed244635d1 100644 --- a/src/openai/resources/beta/responses/responses.py +++ b/src/openai/resources/beta/responses/responses.py @@ -222,11 +222,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, @@ -604,11 +604,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -985,11 +985,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -1364,11 +1364,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, @@ -2104,11 +2104,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, @@ -2486,11 +2486,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -2867,11 +2867,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -3246,11 +3246,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[response_create_params.Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, @@ -4990,11 +4990,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: beta_responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[bool] | Omit = omit, @@ -5193,11 +5193,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[BetaResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: beta_responses_client_event_param.ResponseCreatePromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[beta_responses_client_event_param.ResponseCreateReasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[bool] | Omit = omit, diff --git a/src/openai/resources/chat/completions/completions.py b/src/openai/resources/chat/completions/completions.py index f8e7d40772..8d75855e3a 100644 --- a/src/openai/resources/chat/completions/completions.py +++ b/src/openai/resources/chat/completions/completions.py @@ -109,11 +109,11 @@ def parse( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -269,12 +269,12 @@ def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -559,7 +559,8 @@ def create( verbosity: Constrains the verbosity of the model's response. Lower values will result in more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -597,12 +598,12 @@ def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -886,7 +887,8 @@ def create( verbosity: Constrains the verbosity of the model's response. Lower values will result in more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -924,12 +926,12 @@ def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -1213,7 +1215,8 @@ def create( verbosity: Constrains the verbosity of the model's response. Lower values will result in more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -1250,12 +1253,12 @@ def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -1550,11 +1553,11 @@ def stream( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -1705,11 +1708,11 @@ async def parse( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -1865,12 +1868,12 @@ async def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -2155,7 +2158,8 @@ async def create( verbosity: Constrains the verbosity of the model's response. Lower values will result in more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -2193,12 +2197,12 @@ async def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -2482,7 +2486,8 @@ async def create( verbosity: Constrains the verbosity of the model's response. Lower values will result in more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -2520,12 +2525,12 @@ async def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -2809,7 +2814,8 @@ async def create( verbosity: Constrains the verbosity of the model's response. Lower values will result in more concise responses, while higher values will result in more verbose - responses. Currently supported values are `low`, `medium`, and `high`. + responses. Currently supported values are `low`, `medium`, and `high`. The + default is `medium`. web_search_options: This tool searches the web for relevant results to use in a response. Learn more about the @@ -2846,12 +2852,12 @@ async def create( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, @@ -3146,11 +3152,11 @@ def stream( parallel_tool_calls: bool | Omit = omit, prediction: Optional[ChatCompletionPredictionContentParam] | Omit = omit, presence_penalty: Optional[float] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: completion_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning_effort: Optional[ReasoningEffort] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, seed: Optional[int] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, stop: Union[Optional[str], SequenceNotStr[str], None] | Omit = omit, diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 4fcf33a257..54ce5ee26c 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -147,11 +147,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, @@ -425,11 +425,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -702,11 +702,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -977,11 +977,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, @@ -1088,11 +1088,11 @@ def stream( moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -1131,11 +1131,11 @@ def stream( moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -1293,11 +1293,11 @@ def parse( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, @@ -1970,11 +1970,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Omit = omit, @@ -2248,11 +2248,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -2525,11 +2525,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -2800,11 +2800,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, @@ -2911,11 +2911,11 @@ def stream( moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -2954,11 +2954,11 @@ def stream( moderation: Optional[response_create_params.Moderation] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream_options: Optional[response_create_params.StreamOptions] | Omit = omit, @@ -3115,11 +3115,11 @@ async def parse( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: response_create_params.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[Literal[False]] | Literal[True] | Omit = omit, @@ -4831,11 +4831,11 @@ def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: responses_client_event_param.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[bool] | Omit = omit, @@ -4915,11 +4915,11 @@ async def create( parallel_tool_calls: Optional[bool] | Omit = omit, previous_response_id: Optional[str] | Omit = omit, prompt: Optional[ResponsePromptParam] | Omit = omit, - prompt_cache_key: str | Omit = omit, + prompt_cache_key: Optional[str] | Omit = omit, prompt_cache_options: responses_client_event_param.PromptCacheOptions | Omit = omit, prompt_cache_retention: Optional[Literal["in_memory", "24h"]] | Omit = omit, reasoning: Optional[Reasoning] | Omit = omit, - safety_identifier: str | Omit = omit, + safety_identifier: Optional[str] | Omit = omit, service_tier: Optional[Literal["auto", "default", "flex", "scale", "priority"]] | Omit = omit, store: Optional[bool] | Omit = omit, stream: Optional[bool] | Omit = omit, diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py index eb50bcbcd0..f9bea6200b 100644 --- a/src/openai/types/beta/beta_response.py +++ b/src/openai/types/beta/beta_response.py @@ -201,7 +201,10 @@ class Reasoning(BaseModel): context: Optional[Literal["auto", "current_turn", "all_turns"]] = None """ - Controls which reasoning items are rendered back to the model on later turns. + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + When returned on a response, this is the effective reasoning context mode used for the response. """ diff --git a/src/openai/types/beta/beta_response_error.py b/src/openai/types/beta/beta_response_error.py index c725765d63..0d423fa1fa 100644 --- a/src/openai/types/beta/beta_response_error.py +++ b/src/openai/types/beta/beta_response_error.py @@ -14,6 +14,7 @@ class BetaResponseError(BaseModel): "server_error", "rate_limit_exceeded", "invalid_prompt", + "data_residency_mismatch", "bio_policy", "vector_store_timeout", "invalid_image", diff --git a/src/openai/types/beta/beta_response_text_config.py b/src/openai/types/beta/beta_response_text_config.py index a0ec4e66d9..b3e4d25798 100644 --- a/src/openai/types/beta/beta_response_text_config.py +++ b/src/openai/types/beta/beta_response_text_config.py @@ -39,5 +39,5 @@ class BetaResponseTextConfig(BaseModel): Lower values will result in more concise responses, while higher values will result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. + `medium`, and `high`. The default is `medium`. """ diff --git a/src/openai/types/beta/beta_response_text_config_param.py b/src/openai/types/beta/beta_response_text_config_param.py index 52f2cd6a1a..eb17df7ad9 100644 --- a/src/openai/types/beta/beta_response_text_config_param.py +++ b/src/openai/types/beta/beta_response_text_config_param.py @@ -40,5 +40,5 @@ class BetaResponseTextConfigParam(TypedDict, total=False): Lower values will result in more concise responses, while higher values will result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. + `medium`, and `high`. The default is `medium`. """ diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py index 6450d15260..a83f7038b2 100644 --- a/src/openai/types/beta/beta_responses_client_event.py +++ b/src/openai/types/beta/beta_responses_client_event.py @@ -135,7 +135,10 @@ class ResponseCreateReasoning(BaseModel): context: Optional[Literal["auto", "current_turn", "all_turns"]] = None """ - Controls which reasoning items are rendered back to the model on later turns. + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + When returned on a response, this is the effective reasoning context mode used for the response. """ diff --git a/src/openai/types/beta/beta_responses_client_event_param.py b/src/openai/types/beta/beta_responses_client_event_param.py index adc3e958ac..e5d84f2b4e 100644 --- a/src/openai/types/beta/beta_responses_client_event_param.py +++ b/src/openai/types/beta/beta_responses_client_event_param.py @@ -135,7 +135,10 @@ class ResponseCreateReasoning(TypedDict, total=False): context: Optional[Literal["auto", "current_turn", "all_turns"]] """ - Controls which reasoning items are rendered back to the model on later turns. + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + When returned on a response, this is the effective reasoning context mode used for the response. """ @@ -437,7 +440,7 @@ class ResponseCreate(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ - prompt_cache_key: str + prompt_cache_key: Optional[str] """ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. @@ -485,7 +488,7 @@ class ResponseCreate(TypedDict, total=False): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ - safety_identifier: str + safety_identifier: Optional[str] """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py index 35694e319a..c86103cded 100644 --- a/src/openai/types/beta/response_create_params.py +++ b/src/openai/types/beta/response_create_params.py @@ -256,7 +256,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ - prompt_cache_key: str + prompt_cache_key: Optional[str] """ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. @@ -304,7 +304,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ - safety_identifier: str + safety_identifier: Optional[str] """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely @@ -520,7 +520,10 @@ class Reasoning(TypedDict, total=False): context: Optional[Literal["auto", "current_turn", "all_turns"]] """ - Controls which reasoning items are rendered back to the model on later turns. + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + When returned on a response, this is the effective reasoning context mode used for the response. """ diff --git a/src/openai/types/beta/responses/input_token_count_params.py b/src/openai/types/beta/responses/input_token_count_params.py index 0d3c91e86d..46413246b4 100644 --- a/src/openai/types/beta/responses/input_token_count_params.py +++ b/src/openai/types/beta/responses/input_token_count_params.py @@ -123,7 +123,10 @@ class Reasoning(TypedDict, total=False): context: Optional[Literal["auto", "current_turn", "all_turns"]] """ - Controls which reasoning items are rendered back to the model on later turns. + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + When returned on a response, this is the effective reasoning context mode used for the response. """ @@ -194,7 +197,7 @@ class Text(TypedDict, total=False): Lower values will result in more concise responses, while higher values will result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. + `medium`, and `high`. The default is `medium`. """ diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py index dfccdb0574..ce027e067a 100644 --- a/src/openai/types/chat/completion_create_params.py +++ b/src/openai/types/chat/completion_create_params.py @@ -186,7 +186,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): far, increasing the model's likelihood to talk about new topics. """ - prompt_cache_key: str + prompt_cache_key: Optional[str] """ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. @@ -251,7 +251,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): preferred for models that support it. """ - safety_identifier: str + safety_identifier: Optional[str] """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely @@ -368,7 +368,7 @@ class CompletionCreateParamsBase(TypedDict, total=False): Lower values will result in more concise responses, while higher values will result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. + `medium`, and `high`. The default is `medium`. """ web_search_options: WebSearchOptions diff --git a/src/openai/types/responses/input_token_count_params.py b/src/openai/types/responses/input_token_count_params.py index 78ed01f432..17c350d5ac 100644 --- a/src/openai/types/responses/input_token_count_params.py +++ b/src/openai/types/responses/input_token_count_params.py @@ -143,7 +143,7 @@ class Text(TypedDict, total=False): Lower values will result in more concise responses, while higher values will result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. + `medium`, and `high`. The default is `medium`. """ diff --git a/src/openai/types/responses/response_create_params.py b/src/openai/types/responses/response_create_params.py index f775970eff..76d3b5fde6 100644 --- a/src/openai/types/responses/response_create_params.py +++ b/src/openai/types/responses/response_create_params.py @@ -154,7 +154,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ - prompt_cache_key: str + prompt_cache_key: Optional[str] """ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. @@ -202,7 +202,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ - safety_identifier: str + safety_identifier: Optional[str] """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely diff --git a/src/openai/types/responses/response_error.py b/src/openai/types/responses/response_error.py index b38a9bada8..254a7e3770 100644 --- a/src/openai/types/responses/response_error.py +++ b/src/openai/types/responses/response_error.py @@ -14,6 +14,7 @@ class ResponseError(BaseModel): "server_error", "rate_limit_exceeded", "invalid_prompt", + "data_residency_mismatch", "bio_policy", "vector_store_timeout", "invalid_image", diff --git a/src/openai/types/responses/response_text_config.py b/src/openai/types/responses/response_text_config.py index fbf4da0b03..2095c10a1d 100644 --- a/src/openai/types/responses/response_text_config.py +++ b/src/openai/types/responses/response_text_config.py @@ -39,5 +39,5 @@ class ResponseTextConfig(BaseModel): Lower values will result in more concise responses, while higher values will result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. + `medium`, and `high`. The default is `medium`. """ diff --git a/src/openai/types/responses/response_text_config_param.py b/src/openai/types/responses/response_text_config_param.py index 9cd54765b0..fadb424bb1 100644 --- a/src/openai/types/responses/response_text_config_param.py +++ b/src/openai/types/responses/response_text_config_param.py @@ -40,5 +40,5 @@ class ResponseTextConfigParam(TypedDict, total=False): Lower values will result in more concise responses, while higher values will result in more verbose responses. Currently supported values are `low`, - `medium`, and `high`. + `medium`, and `high`. The default is `medium`. """ diff --git a/src/openai/types/responses/responses_client_event_param.py b/src/openai/types/responses/responses_client_event_param.py index 818248b818..4d7981d2df 100644 --- a/src/openai/types/responses/responses_client_event_param.py +++ b/src/openai/types/responses/responses_client_event_param.py @@ -258,7 +258,7 @@ class ResponsesClientEventParam(TypedDict, total=False): [Learn more](https://platform.openai.com/docs/guides/text?api-mode=responses#reusable-prompts). """ - prompt_cache_key: str + prompt_cache_key: Optional[str] """ Used by OpenAI to cache responses for similar requests to optimize your cache hit rates. Replaces the `user` field. @@ -306,7 +306,7 @@ class ResponsesClientEventParam(TypedDict, total=False): [reasoning models](https://platform.openai.com/docs/guides/reasoning). """ - safety_identifier: str + safety_identifier: Optional[str] """ A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. The IDs should be a string that uniquely diff --git a/src/openai/types/shared/reasoning.py b/src/openai/types/shared/reasoning.py index 7dddbb9b44..c1fe873ae0 100644 --- a/src/openai/types/shared/reasoning.py +++ b/src/openai/types/shared/reasoning.py @@ -18,7 +18,10 @@ class Reasoning(BaseModel): context: Optional[Literal["auto", "current_turn", "all_turns"]] = None """ - Controls which reasoning items are rendered back to the model on later turns. + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + When returned on a response, this is the effective reasoning context mode used for the response. """ diff --git a/src/openai/types/shared_params/reasoning.py b/src/openai/types/shared_params/reasoning.py index 70cf624f0e..3dd3fdf5f1 100644 --- a/src/openai/types/shared_params/reasoning.py +++ b/src/openai/types/shared_params/reasoning.py @@ -19,7 +19,10 @@ class Reasoning(TypedDict, total=False): context: Optional[Literal["auto", "current_turn", "all_turns"]] """ - Controls which reasoning items are rendered back to the model on later turns. + Controls which reasoning items are rendered back to the model on later turns. If + omitted or set to `auto`, the model determines the context mode. The `gpt-5.6` + model family defaults to `all_turns`; earlier models default to `current_turn`. + When returned on a response, this is the effective reasoning context mode used for the response. """ From cbd2fba7c48b6376e80775cb191e2eda1d476c61 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 16:15:58 +0000 Subject: [PATCH 407/408] feat(api): add support for `spend_limit` admin apis --- .stats.yml | 8 +- api.md | 28 ++ .../resources/admin/organization/__init__.py | 14 + .../admin/organization/organization.py | 32 ++ .../admin/organization/projects/__init__.py | 14 + .../admin/organization/projects/projects.py | 32 ++ .../organization/projects/spend_limit.py | 380 ++++++++++++++++++ .../admin/organization/spend_limit.py | 318 +++++++++++++++ .../types/admin/organization/__init__.py | 3 + .../organization/organization_spend_limit.py | 37 ++ .../organization_spend_limit_deleted.py | 17 + .../admin/organization/projects/__init__.py | 3 + .../projects/project_spend_limit.py | 37 ++ .../projects/project_spend_limit_deleted.py | 17 + .../projects/spend_limit_update_params.py | 21 + .../organization/spend_limit_update_params.py | 21 + .../organization/projects/test_spend_limit.py | 279 +++++++++++++ .../admin/organization/test_spend_limit.py | 201 +++++++++ 18 files changed, 1458 insertions(+), 4 deletions(-) create mode 100644 src/openai/resources/admin/organization/projects/spend_limit.py create mode 100644 src/openai/resources/admin/organization/spend_limit.py create mode 100644 src/openai/types/admin/organization/organization_spend_limit.py create mode 100644 src/openai/types/admin/organization/organization_spend_limit_deleted.py create mode 100644 src/openai/types/admin/organization/projects/project_spend_limit.py create mode 100644 src/openai/types/admin/organization/projects/project_spend_limit_deleted.py create mode 100644 src/openai/types/admin/organization/projects/spend_limit_update_params.py create mode 100644 src/openai/types/admin/organization/spend_limit_update_params.py create mode 100644 tests/api_resources/admin/organization/projects/test_spend_limit.py create mode 100644 tests/api_resources/admin/organization/test_spend_limit.py diff --git a/.stats.yml b/.stats.yml index 39bf33e59e..61025cba01 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 272 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-5555d8e0f800f68d951749454d94fa8b899fc3722550657578b1d8a9fd3322e8.yml -openapi_spec_hash: 754a2e573085f55d60f02dbc3a5ef2b1 -config_hash: 896f0f71bc7d1ee09435813cdef97523 +configured_endpoints: 278 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai/openai-0650ebae454b4200491980d7663cb7ceabcff90dcb3ed9f67c3bca3e861a2bf6.yml +openapi_spec_hash: e9576bced964246b7e685a5ad30afffa +config_hash: 43e311595bcc4fb6b32eba0684de48d8 diff --git a/api.md b/api.md index 8ee735e5df..0124bff7d0 100644 --- a/api.md +++ b/api.md @@ -1102,6 +1102,20 @@ Methods: - client.admin.organization.data_retention.retrieve() -> OrganizationDataRetention - client.admin.organization.data_retention.update(\*\*params) -> OrganizationDataRetention +### SpendLimit + +Types: + +```python +from openai.types.admin.organization import OrganizationSpendLimit, OrganizationSpendLimitDeleted +``` + +Methods: + +- client.admin.organization.spend_limit.retrieve() -> OrganizationSpendLimit +- client.admin.organization.spend_limit.update(\*\*params) -> OrganizationSpendLimit +- client.admin.organization.spend_limit.delete() -> OrganizationSpendLimitDeleted + ### SpendAlerts Types: @@ -1347,6 +1361,20 @@ Methods: - client.admin.organization.projects.data_retention.retrieve(project_id) -> ProjectDataRetention - client.admin.organization.projects.data_retention.update(project_id, \*\*params) -> ProjectDataRetention +#### SpendLimit + +Types: + +```python +from openai.types.admin.organization.projects import ProjectSpendLimit, ProjectSpendLimitDeleted +``` + +Methods: + +- client.admin.organization.projects.spend_limit.retrieve(project_id) -> ProjectSpendLimit +- client.admin.organization.projects.spend_limit.update(project_id, \*\*params) -> ProjectSpendLimit +- client.admin.organization.projects.spend_limit.delete(project_id) -> ProjectSpendLimitDeleted + #### SpendAlerts Types: diff --git a/src/openai/resources/admin/organization/__init__.py b/src/openai/resources/admin/organization/__init__.py index d87fbbc81d..cb306466ea 100644 --- a/src/openai/resources/admin/organization/__init__.py +++ b/src/openai/resources/admin/organization/__init__.py @@ -56,6 +56,14 @@ AuditLogsWithStreamingResponse, AsyncAuditLogsWithStreamingResponse, ) +from .spend_limit import ( + SpendLimit, + AsyncSpendLimit, + SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse, +) from .certificates import ( Certificates, AsyncCertificates, @@ -146,6 +154,12 @@ "AsyncDataRetentionWithRawResponse", "DataRetentionWithStreamingResponse", "AsyncDataRetentionWithStreamingResponse", + "SpendLimit", + "AsyncSpendLimit", + "SpendLimitWithRawResponse", + "AsyncSpendLimitWithRawResponse", + "SpendLimitWithStreamingResponse", + "AsyncSpendLimitWithStreamingResponse", "SpendAlerts", "AsyncSpendAlerts", "SpendAlertsWithRawResponse", diff --git a/src/openai/resources/admin/organization/organization.py b/src/openai/resources/admin/organization/organization.py index 62bba207cb..7100cbcbfb 100644 --- a/src/openai/resources/admin/organization/organization.py +++ b/src/openai/resources/admin/organization/organization.py @@ -35,6 +35,14 @@ AuditLogsWithStreamingResponse, AsyncAuditLogsWithStreamingResponse, ) +from .spend_limit import ( + SpendLimit, + AsyncSpendLimit, + SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse, +) from .users.users import ( Users, AsyncUsers, @@ -130,6 +138,10 @@ def roles(self) -> Roles: def data_retention(self) -> DataRetention: return DataRetention(self._client) + @cached_property + def spend_limit(self) -> SpendLimit: + return SpendLimit(self._client) + @cached_property def spend_alerts(self) -> SpendAlerts: return SpendAlerts(self._client) @@ -196,6 +208,10 @@ def roles(self) -> AsyncRoles: def data_retention(self) -> AsyncDataRetention: return AsyncDataRetention(self._client) + @cached_property + def spend_limit(self) -> AsyncSpendLimit: + return AsyncSpendLimit(self._client) + @cached_property def spend_alerts(self) -> AsyncSpendAlerts: return AsyncSpendAlerts(self._client) @@ -265,6 +281,10 @@ def roles(self) -> RolesWithRawResponse: def data_retention(self) -> DataRetentionWithRawResponse: return DataRetentionWithRawResponse(self._organization.data_retention) + @cached_property + def spend_limit(self) -> SpendLimitWithRawResponse: + return SpendLimitWithRawResponse(self._organization.spend_limit) + @cached_property def spend_alerts(self) -> SpendAlertsWithRawResponse: return SpendAlertsWithRawResponse(self._organization.spend_alerts) @@ -315,6 +335,10 @@ def roles(self) -> AsyncRolesWithRawResponse: def data_retention(self) -> AsyncDataRetentionWithRawResponse: return AsyncDataRetentionWithRawResponse(self._organization.data_retention) + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithRawResponse: + return AsyncSpendLimitWithRawResponse(self._organization.spend_limit) + @cached_property def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: return AsyncSpendAlertsWithRawResponse(self._organization.spend_alerts) @@ -365,6 +389,10 @@ def roles(self) -> RolesWithStreamingResponse: def data_retention(self) -> DataRetentionWithStreamingResponse: return DataRetentionWithStreamingResponse(self._organization.data_retention) + @cached_property + def spend_limit(self) -> SpendLimitWithStreamingResponse: + return SpendLimitWithStreamingResponse(self._organization.spend_limit) + @cached_property def spend_alerts(self) -> SpendAlertsWithStreamingResponse: return SpendAlertsWithStreamingResponse(self._organization.spend_alerts) @@ -415,6 +443,10 @@ def roles(self) -> AsyncRolesWithStreamingResponse: def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: return AsyncDataRetentionWithStreamingResponse(self._organization.data_retention) + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithStreamingResponse: + return AsyncSpendLimitWithStreamingResponse(self._organization.spend_limit) + @cached_property def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: return AsyncSpendAlertsWithStreamingResponse(self._organization.spend_alerts) diff --git a/src/openai/resources/admin/organization/projects/__init__.py b/src/openai/resources/admin/organization/projects/__init__.py index 3bc97170fd..f585c0eabb 100644 --- a/src/openai/resources/admin/organization/projects/__init__.py +++ b/src/openai/resources/admin/organization/projects/__init__.py @@ -48,6 +48,14 @@ RateLimitsWithStreamingResponse, AsyncRateLimitsWithStreamingResponse, ) +from .spend_limit import ( + SpendLimit, + AsyncSpendLimit, + SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse, +) from .certificates import ( Certificates, AsyncCertificates, @@ -152,6 +160,12 @@ "AsyncDataRetentionWithRawResponse", "DataRetentionWithStreamingResponse", "AsyncDataRetentionWithStreamingResponse", + "SpendLimit", + "AsyncSpendLimit", + "SpendLimitWithRawResponse", + "AsyncSpendLimitWithRawResponse", + "SpendLimitWithStreamingResponse", + "AsyncSpendLimitWithStreamingResponse", "SpendAlerts", "AsyncSpendAlerts", "SpendAlertsWithRawResponse", diff --git a/src/openai/resources/admin/organization/projects/projects.py b/src/openai/resources/admin/organization/projects/projects.py index ea2172b594..36d8b0805a 100644 --- a/src/openai/resources/admin/organization/projects/projects.py +++ b/src/openai/resources/admin/organization/projects/projects.py @@ -34,6 +34,14 @@ RateLimitsWithStreamingResponse, AsyncRateLimitsWithStreamingResponse, ) +from .spend_limit import ( + SpendLimit, + AsyncSpendLimit, + SpendLimitWithRawResponse, + AsyncSpendLimitWithRawResponse, + SpendLimitWithStreamingResponse, + AsyncSpendLimitWithStreamingResponse, +) from .users.users import ( Users, AsyncUsers, @@ -145,6 +153,10 @@ def roles(self) -> Roles: def data_retention(self) -> DataRetention: return DataRetention(self._client) + @cached_property + def spend_limit(self) -> SpendLimit: + return SpendLimit(self._client) + @cached_property def spend_alerts(self) -> SpendAlerts: return SpendAlerts(self._client) @@ -454,6 +466,10 @@ def roles(self) -> AsyncRoles: def data_retention(self) -> AsyncDataRetention: return AsyncDataRetention(self._client) + @cached_property + def spend_limit(self) -> AsyncSpendLimit: + return AsyncSpendLimit(self._client) + @cached_property def spend_alerts(self) -> AsyncSpendAlerts: return AsyncSpendAlerts(self._client) @@ -782,6 +798,10 @@ def roles(self) -> RolesWithRawResponse: def data_retention(self) -> DataRetentionWithRawResponse: return DataRetentionWithRawResponse(self._projects.data_retention) + @cached_property + def spend_limit(self) -> SpendLimitWithRawResponse: + return SpendLimitWithRawResponse(self._projects.spend_limit) + @cached_property def spend_alerts(self) -> SpendAlertsWithRawResponse: return SpendAlertsWithRawResponse(self._projects.spend_alerts) @@ -847,6 +867,10 @@ def roles(self) -> AsyncRolesWithRawResponse: def data_retention(self) -> AsyncDataRetentionWithRawResponse: return AsyncDataRetentionWithRawResponse(self._projects.data_retention) + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithRawResponse: + return AsyncSpendLimitWithRawResponse(self._projects.spend_limit) + @cached_property def spend_alerts(self) -> AsyncSpendAlertsWithRawResponse: return AsyncSpendAlertsWithRawResponse(self._projects.spend_alerts) @@ -912,6 +936,10 @@ def roles(self) -> RolesWithStreamingResponse: def data_retention(self) -> DataRetentionWithStreamingResponse: return DataRetentionWithStreamingResponse(self._projects.data_retention) + @cached_property + def spend_limit(self) -> SpendLimitWithStreamingResponse: + return SpendLimitWithStreamingResponse(self._projects.spend_limit) + @cached_property def spend_alerts(self) -> SpendAlertsWithStreamingResponse: return SpendAlertsWithStreamingResponse(self._projects.spend_alerts) @@ -977,6 +1005,10 @@ def roles(self) -> AsyncRolesWithStreamingResponse: def data_retention(self) -> AsyncDataRetentionWithStreamingResponse: return AsyncDataRetentionWithStreamingResponse(self._projects.data_retention) + @cached_property + def spend_limit(self) -> AsyncSpendLimitWithStreamingResponse: + return AsyncSpendLimitWithStreamingResponse(self._projects.spend_limit) + @cached_property def spend_alerts(self) -> AsyncSpendAlertsWithStreamingResponse: return AsyncSpendAlertsWithStreamingResponse(self._projects.spend_alerts) diff --git a/src/openai/resources/admin/organization/projects/spend_limit.py b/src/openai/resources/admin/organization/projects/spend_limit.py new file mode 100644 index 0000000000..baca973214 --- /dev/null +++ b/src/openai/resources/admin/organization/projects/spend_limit.py @@ -0,0 +1,380 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..... import _legacy_response +from ....._types import Body, Query, Headers, NotGiven, not_given +from ....._utils import path_template, maybe_transform, async_maybe_transform +from ....._compat import cached_property +from ....._resource import SyncAPIResource, AsyncAPIResource +from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ....._base_client import make_request_options +from .....types.admin.organization.projects import spend_limit_update_params +from .....types.admin.organization.projects.project_spend_limit import ProjectSpendLimit +from .....types.admin.organization.projects.project_spend_limit_deleted import ProjectSpendLimitDeleted + +__all__ = ["SpendLimit", "AsyncSpendLimit"] + + +class SpendLimit(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendLimitWithStreamingResponse(self) + + def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Get a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._get( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + def update( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Create or replace a project's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._post( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimitDeleted: + """ + Delete a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return self._delete( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimitDeleted, + ) + + +class AsyncSpendLimit(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendLimitWithStreamingResponse(self) + + async def retrieve( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Get a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._get( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + async def update( + self, + project_id: str, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimit: + """ + Create or replace a project's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._post( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimit, + ) + + async def delete( + self, + project_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectSpendLimitDeleted: + """ + Delete a project's hard spend limit. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not project_id: + raise ValueError(f"Expected a non-empty value for `project_id` but received {project_id!r}") + return await self._delete( + path_template("/organization/projects/{project_id}/spend_limit", project_id=project_id), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=ProjectSpendLimitDeleted, + ) + + +class SpendLimitWithRawResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithRawResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_limit.delete, + ) + + +class SpendLimitWithStreamingResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = to_streamed_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithStreamingResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = async_to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = async_to_streamed_response_wrapper( + spend_limit.delete, + ) diff --git a/src/openai/resources/admin/organization/spend_limit.py b/src/openai/resources/admin/organization/spend_limit.py new file mode 100644 index 0000000000..3b9394f615 --- /dev/null +++ b/src/openai/resources/admin/organization/spend_limit.py @@ -0,0 +1,318 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from .... import _legacy_response +from ...._types import Body, Query, Headers, NotGiven, not_given +from ...._utils import maybe_transform, async_maybe_transform +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper +from ...._base_client import make_request_options +from ....types.admin.organization import spend_limit_update_params +from ....types.admin.organization.organization_spend_limit import OrganizationSpendLimit +from ....types.admin.organization.organization_spend_limit_deleted import OrganizationSpendLimitDeleted + +__all__ = ["SpendLimit", "AsyncSpendLimit"] + + +class SpendLimit(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return SpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return SpendLimitWithStreamingResponse(self) + + def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """Get the organization's hard spend limit.""" + return self._get( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + def update( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """ + Create or replace the organization's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/organization/spend_limit", + body=maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + def delete( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimitDeleted: + """Delete the organization's hard spend limit.""" + return self._delete( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimitDeleted, + ) + + +class AsyncSpendLimit(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSpendLimitWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/openai/openai-python#accessing-raw-response-data-eg-headers + """ + return AsyncSpendLimitWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSpendLimitWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/openai/openai-python#with_streaming_response + """ + return AsyncSpendLimitWithStreamingResponse(self) + + async def retrieve( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """Get the organization's hard spend limit.""" + return await self._get( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + async def update( + self, + *, + currency: Literal["USD"], + interval: Literal["month"], + threshold_amount: int, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimit: + """ + Create or replace the organization's hard spend limit. + + Args: + currency: The currency for the threshold amount. Currently, only `USD` is supported. + + interval: The time interval for evaluating spend against the threshold. Currently, only + `month` is supported. + + threshold_amount: The hard spend limit amount, in cents. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/organization/spend_limit", + body=await async_maybe_transform( + { + "currency": currency, + "interval": interval, + "threshold_amount": threshold_amount, + }, + spend_limit_update_params.SpendLimitUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimit, + ) + + async def delete( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> OrganizationSpendLimitDeleted: + """Delete the organization's hard spend limit.""" + return await self._delete( + "/organization/spend_limit", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + security={"admin_api_key_auth": True}, + ), + cast_to=OrganizationSpendLimitDeleted, + ) + + +class SpendLimitWithRawResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.to_raw_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithRawResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = _legacy_response.async_to_raw_response_wrapper( + spend_limit.retrieve, + ) + self.update = _legacy_response.async_to_raw_response_wrapper( + spend_limit.update, + ) + self.delete = _legacy_response.async_to_raw_response_wrapper( + spend_limit.delete, + ) + + +class SpendLimitWithStreamingResponse: + def __init__(self, spend_limit: SpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = to_streamed_response_wrapper( + spend_limit.delete, + ) + + +class AsyncSpendLimitWithStreamingResponse: + def __init__(self, spend_limit: AsyncSpendLimit) -> None: + self._spend_limit = spend_limit + + self.retrieve = async_to_streamed_response_wrapper( + spend_limit.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + spend_limit.update, + ) + self.delete = async_to_streamed_response_wrapper( + spend_limit.delete, + ) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index d14d8dce41..4efdf659e2 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -37,6 +37,7 @@ from .spend_alert_list_params import SpendAlertListParams as SpendAlertListParams from .usage_embeddings_params import UsageEmbeddingsParams as UsageEmbeddingsParams from .organization_spend_alert import OrganizationSpendAlert as OrganizationSpendAlert +from .organization_spend_limit import OrganizationSpendLimit as OrganizationSpendLimit from .usage_completions_params import UsageCompletionsParams as UsageCompletionsParams from .usage_moderations_params import UsageModerationsParams as UsageModerationsParams from .admin_api_key_list_params import AdminAPIKeyListParams as AdminAPIKeyListParams @@ -45,6 +46,7 @@ from .certificate_update_params import CertificateUpdateParams as CertificateUpdateParams from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams +from .spend_limit_update_params import SpendLimitUpdateParams as SpendLimitUpdateParams from .usage_embeddings_response import UsageEmbeddingsResponse as UsageEmbeddingsResponse from .usage_completions_response import UsageCompletionsResponse as UsageCompletionsResponse from .usage_moderations_response import UsageModerationsResponse as UsageModerationsResponse @@ -67,6 +69,7 @@ from .certificate_deactivate_response import CertificateDeactivateResponse as CertificateDeactivateResponse from .usage_web_search_calls_response import UsageWebSearchCallsResponse as UsageWebSearchCallsResponse from .organization_spend_alert_deleted import OrganizationSpendAlertDeleted as OrganizationSpendAlertDeleted +from .organization_spend_limit_deleted import OrganizationSpendLimitDeleted as OrganizationSpendLimitDeleted from .usage_file_search_calls_response import UsageFileSearchCallsResponse as UsageFileSearchCallsResponse from .usage_audio_transcriptions_params import UsageAudioTranscriptionsParams as UsageAudioTranscriptionsParams from .usage_audio_transcriptions_response import UsageAudioTranscriptionsResponse as UsageAudioTranscriptionsResponse diff --git a/src/openai/types/admin/organization/organization_spend_limit.py b/src/openai/types/admin/organization/organization_spend_limit.py new file mode 100644 index 0000000000..89de914d27 --- /dev/null +++ b/src/openai/types/admin/organization/organization_spend_limit.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendLimit", "Enforcement"] + + +class Enforcement(BaseModel): + """The current enforcement state of the hard spend limit.""" + + status: Union[str, Literal["inactive", "enforcing"]] + """Whether the hard spend limit is currently enforcing.""" + + +class OrganizationSpendLimit(BaseModel): + """Represents a hard spend limit configured at the organization level.""" + + currency: Union[str, Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + enforcement: Enforcement + """The current enforcement state of the hard spend limit.""" + + interval: Union[str, Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + object: Literal["organization.spend_limit"] + """The object type, which is always `organization.spend_limit`.""" + + threshold_amount: int + """The hard spend limit amount, in cents.""" diff --git a/src/openai/types/admin/organization/organization_spend_limit_deleted.py b/src/openai/types/admin/organization/organization_spend_limit_deleted.py new file mode 100644 index 0000000000..2ded2abedf --- /dev/null +++ b/src/openai/types/admin/organization/organization_spend_limit_deleted.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["OrganizationSpendLimitDeleted"] + + +class OrganizationSpendLimitDeleted(BaseModel): + """Confirmation payload returned after deleting an organization hard spend limit.""" + + deleted: bool + """Whether the hard spend limit was deleted.""" + + object: Literal["organization.spend_limit.deleted"] + """The object type, which is always `organization.spend_limit.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/__init__.py b/src/openai/types/admin/organization/projects/__init__.py index 1c510be58d..f92f701b50 100644 --- a/src/openai/types/admin/organization/projects/__init__.py +++ b/src/openai/types/admin/organization/projects/__init__.py @@ -16,6 +16,7 @@ from .api_key_list_params import APIKeyListParams as APIKeyListParams from .group_create_params import GroupCreateParams as GroupCreateParams from .project_spend_alert import ProjectSpendAlert as ProjectSpendAlert +from .project_spend_limit import ProjectSpendLimit as ProjectSpendLimit from .role_delete_response import RoleDeleteResponse as RoleDeleteResponse from .user_delete_response import UserDeleteResponse as UserDeleteResponse from .group_delete_response import GroupDeleteResponse as GroupDeleteResponse @@ -29,8 +30,10 @@ from .project_model_permissions import ProjectModelPermissions as ProjectModelPermissions from .spend_alert_create_params import SpendAlertCreateParams as SpendAlertCreateParams from .spend_alert_update_params import SpendAlertUpdateParams as SpendAlertUpdateParams +from .spend_limit_update_params import SpendLimitUpdateParams as SpendLimitUpdateParams from .certificate_activate_params import CertificateActivateParams as CertificateActivateParams from .project_spend_alert_deleted import ProjectSpendAlertDeleted as ProjectSpendAlertDeleted +from .project_spend_limit_deleted import ProjectSpendLimitDeleted as ProjectSpendLimitDeleted from .service_account_list_params import ServiceAccountListParams as ServiceAccountListParams from .data_retention_update_params import DataRetentionUpdateParams as DataRetentionUpdateParams from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse diff --git a/src/openai/types/admin/organization/projects/project_spend_limit.py b/src/openai/types/admin/organization/projects/project_spend_limit.py new file mode 100644 index 0000000000..99971d727c --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_spend_limit.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Union +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendLimit", "Enforcement"] + + +class Enforcement(BaseModel): + """The current enforcement state of the hard spend limit.""" + + status: Union[str, Literal["inactive", "enforcing"]] + """Whether the hard spend limit is currently enforcing.""" + + +class ProjectSpendLimit(BaseModel): + """Represents a hard spend limit configured at the project level.""" + + currency: Union[str, Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + enforcement: Enforcement + """The current enforcement state of the hard spend limit.""" + + interval: Union[str, Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + object: Literal["project.spend_limit"] + """The object type, which is always `project.spend_limit`.""" + + threshold_amount: int + """The hard spend limit amount, in cents.""" diff --git a/src/openai/types/admin/organization/projects/project_spend_limit_deleted.py b/src/openai/types/admin/organization/projects/project_spend_limit_deleted.py new file mode 100644 index 0000000000..c6dcc55550 --- /dev/null +++ b/src/openai/types/admin/organization/projects/project_spend_limit_deleted.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ....._models import BaseModel + +__all__ = ["ProjectSpendLimitDeleted"] + + +class ProjectSpendLimitDeleted(BaseModel): + """Confirmation payload returned after deleting a project hard spend limit.""" + + deleted: bool + """Whether the hard spend limit was deleted.""" + + object: Literal["project.spend_limit.deleted"] + """The object type, which is always `project.spend_limit.deleted`.""" diff --git a/src/openai/types/admin/organization/projects/spend_limit_update_params.py b/src/openai/types/admin/organization/projects/spend_limit_update_params.py new file mode 100644 index 0000000000..34d75f4e41 --- /dev/null +++ b/src/openai/types/admin/organization/projects/spend_limit_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SpendLimitUpdateParams"] + + +class SpendLimitUpdateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + threshold_amount: Required[int] + """The hard spend limit amount, in cents.""" diff --git a/src/openai/types/admin/organization/spend_limit_update_params.py b/src/openai/types/admin/organization/spend_limit_update_params.py new file mode 100644 index 0000000000..34d75f4e41 --- /dev/null +++ b/src/openai/types/admin/organization/spend_limit_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["SpendLimitUpdateParams"] + + +class SpendLimitUpdateParams(TypedDict, total=False): + currency: Required[Literal["USD"]] + """The currency for the threshold amount. Currently, only `USD` is supported.""" + + interval: Required[Literal["month"]] + """The time interval for evaluating spend against the threshold. + + Currently, only `month` is supported. + """ + + threshold_amount: Required[int] + """The hard spend limit amount, in cents.""" diff --git a/tests/api_resources/admin/organization/projects/test_spend_limit.py b/tests/api_resources/admin/organization/projects/test_spend_limit.py new file mode 100644 index 0000000000..625e0efb5e --- /dev/null +++ b/tests/api_resources/admin/organization/projects/test_spend_limit.py @@ -0,0 +1,279 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization.projects import ( + ProjectSpendLimit, + ProjectSpendLimitDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSpendLimit: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + spend_limit = client.admin.organization.projects.spend_limit.retrieve( + "proj_123", + ) + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_limit.with_raw_response.retrieve( + "proj_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_limit.with_streaming_response.retrieve( + "proj_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_limit.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + spend_limit = client.admin.organization.projects.spend_limit.update( + project_id="proj_123", + currency="USD", + interval="month", + threshold_amount=1, + ) + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_limit.with_raw_response.update( + project_id="proj_123", + currency="USD", + interval="month", + threshold_amount=1, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_limit.with_streaming_response.update( + project_id="proj_123", + currency="USD", + interval="month", + threshold_amount=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_limit.with_raw_response.update( + project_id="", + currency="USD", + interval="month", + threshold_amount=1, + ) + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + spend_limit = client.admin.organization.projects.spend_limit.delete( + "proj_123", + ) + assert_matches_type(ProjectSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.projects.spend_limit.with_raw_response.delete( + "proj_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.projects.spend_limit.with_streaming_response.delete( + "proj_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimitDeleted, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: OpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + client.admin.organization.projects.spend_limit.with_raw_response.delete( + "", + ) + + +class TestAsyncSpendLimit: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + spend_limit = await async_client.admin.organization.projects.spend_limit.retrieve( + "proj_123", + ) + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_limit.with_raw_response.retrieve( + "proj_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_limit.with_streaming_response.retrieve( + "proj_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = await response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_limit.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + spend_limit = await async_client.admin.organization.projects.spend_limit.update( + project_id="proj_123", + currency="USD", + interval="month", + threshold_amount=1, + ) + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_limit.with_raw_response.update( + project_id="proj_123", + currency="USD", + interval="month", + threshold_amount=1, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_limit.with_streaming_response.update( + project_id="proj_123", + currency="USD", + interval="month", + threshold_amount=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = await response.parse() + assert_matches_type(ProjectSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_limit.with_raw_response.update( + project_id="", + currency="USD", + interval="month", + threshold_amount=1, + ) + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + spend_limit = await async_client.admin.organization.projects.spend_limit.delete( + "proj_123", + ) + assert_matches_type(ProjectSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.projects.spend_limit.with_raw_response.delete( + "proj_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(ProjectSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.projects.spend_limit.with_streaming_response.delete( + "proj_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = await response.parse() + assert_matches_type(ProjectSpendLimitDeleted, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpenAI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `project_id` but received ''"): + await async_client.admin.organization.projects.spend_limit.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/admin/organization/test_spend_limit.py b/tests/api_resources/admin/organization/test_spend_limit.py new file mode 100644 index 0000000000..4dd601a4d4 --- /dev/null +++ b/tests/api_resources/admin/organization/test_spend_limit.py @@ -0,0 +1,201 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from openai import OpenAI, AsyncOpenAI +from tests.utils import assert_matches_type +from openai.types.admin.organization import ( + OrganizationSpendLimit, + OrganizationSpendLimitDeleted, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSpendLimit: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: OpenAI) -> None: + spend_limit = client.admin.organization.spend_limit.retrieve() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: OpenAI) -> None: + response = client.admin.organization.spend_limit.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: OpenAI) -> None: + with client.admin.organization.spend_limit.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_update(self, client: OpenAI) -> None: + spend_limit = client.admin.organization.spend_limit.update( + currency="USD", + interval="month", + threshold_amount=1, + ) + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: OpenAI) -> None: + response = client.admin.organization.spend_limit.with_raw_response.update( + currency="USD", + interval="month", + threshold_amount=1, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: OpenAI) -> None: + with client.admin.organization.spend_limit.with_streaming_response.update( + currency="USD", + interval="month", + threshold_amount=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: OpenAI) -> None: + spend_limit = client.admin.organization.spend_limit.delete() + assert_matches_type(OrganizationSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + def test_raw_response_delete(self, client: OpenAI) -> None: + response = client.admin.organization.spend_limit.with_raw_response.delete() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + def test_streaming_response_delete(self, client: OpenAI) -> None: + with client.admin.organization.spend_limit.with_streaming_response.delete() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimitDeleted, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSpendLimit: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None: + spend_limit = await async_client.admin.organization.spend_limit.retrieve() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_limit.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_limit.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = await response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_update(self, async_client: AsyncOpenAI) -> None: + spend_limit = await async_client.admin.organization.spend_limit.update( + currency="USD", + interval="month", + threshold_amount=1, + ) + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_limit.with_raw_response.update( + currency="USD", + interval="month", + threshold_amount=1, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_limit.with_streaming_response.update( + currency="USD", + interval="month", + threshold_amount=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = await response.parse() + assert_matches_type(OrganizationSpendLimit, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncOpenAI) -> None: + spend_limit = await async_client.admin.organization.spend_limit.delete() + assert_matches_type(OrganizationSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpenAI) -> None: + response = await async_client.admin.organization.spend_limit.with_raw_response.delete() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + spend_limit = response.parse() + assert_matches_type(OrganizationSpendLimitDeleted, spend_limit, path=["response"]) + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpenAI) -> None: + async with async_client.admin.organization.spend_limit.with_streaming_response.delete() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + spend_limit = await response.parse() + assert_matches_type(OrganizationSpendLimitDeleted, spend_limit, path=["response"]) + + assert cast(Any, response.is_closed) is True From 8a6adcb3b88d89462a0fc5a81f50d9775c9ca138 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 16:16:55 +0000 Subject: [PATCH 408/408] release: 2.48.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/openai/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3ab3b9e3de..d0e1cca504 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.47.0" + ".": "2.48.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 90e3d203a7..683068cda9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 2.48.0 (2026-07-23) + +Full Changelog: [v2.47.0...v2.48.0](https://github.com/openai/openai-python/compare/v2.47.0...v2.48.0) + +### Features + +* **api:** accept `None` for prompt_cache_key/safety_identifier ([36820e6](https://github.com/openai/openai-python/commit/36820e69ba6cf1dc3f8d411e48a36e56084fbbc4)) +* **api:** add support for `spend_limit` admin apis ([1ff13af](https://github.com/openai/openai-python/commit/1ff13afb6e831394036686a1c352bbec6d704c68)) + ## 2.47.0 (2026-07-21) Full Changelog: [v2.46.0...v2.47.0](https://github.com/openai/openai-python/compare/v2.46.0...v2.47.0) diff --git a/pyproject.toml b/pyproject.toml index c99f41b56f..5032fa5a30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.47.0" +version = "2.48.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_version.py b/src/openai/_version.py index 33a4084e62..8c2ee06274 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openai" -__version__ = "2.47.0" # x-release-please-version +__version__ = "2.48.0" # x-release-please-version